
문제 출처
https://school.programmers.co.kr/learn/courses/30/lessons/84021
프로그래머스
코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.
programmers.co.kr
문제 풀이
요약
1. game_board의 빈 영역(0)과 table의 블록 영역(1)을 구한다
2. 빈 영역에 블록이 끼워지는지 회전하며 최대 4번 비교한다
3. 블록이 빈 영역에 끼워질 경우 결과값에 블록 크기를 누적하고, 사용처리한다 (2-3 반복)
① game_board 의 빈 영역(0), table 에 블록 영역(1)을 BFS 로 구한다
List<List<int[]>> emptySpace = new ArrayList<>();
List<List<int[]>> blocks = new ArrayList<>();
int row = game_board.length;
int col = game_board[0].length;
boolean[][] visited = new boolean[row][col];
for(int i = 0; i < row; i++) {
    for(int j = 0; j < col; j++) {
        if(game_board[i][j] == 0 && !visited[i][j]) {
            emptySpace.add(extractShape(i, j, row, col, game_board, visited, 0));
        }
    }
}
row = table.length;
col = table[0].length;
visited = new boolean[row][col];
for(int i = 0; i < row; i++) {
    for(int j = 0; j < col; j++) {
        if(table[i][j] == 1 && !visited[i][j]) {
            blocks.add(extractShape(i, j, row, col, table, visited, 1));
        }
    }
}
extractShape는 정말 전형적인 BFS 풀이로 영역을 List<int[]> result에 담으면서 방문 처리를 한다
private static int[][] DIR = {
    {0, 1},
    {0, -1},
    {1, 0},
    {-1, 0}
};
private List<int[]> extractShape(int startX, int startY, int row, int col, int[][] board, boolean[][] visited, int target) {
    List<int[]> result = new ArrayList<>();
    Deque<int[]> que = new ArrayDeque<>();
    que.add(new int[] {startX, startY});
    visited[startX][startY] = true;
    while(!que.isEmpty()) {
        int[] cur = que.poll();
        result.add(cur);
        for(int i = 0; i < 4; i++) {
            int dx = cur[0] + DIR[i][0];
            int dy = cur[1] + DIR[i][1];
            if(dx < 0 || dy < 0 || dx >= row || dy >= col) continue;
            if(visited[dx][dy]) continue;
            if(board[dx][dy] != target) continue;
            visited[dx][dy] = true;
            que.add(new int[] {dx, dy});
        }
    }
    return normalize(result);
}
이때 빈 영역과 블록 영역을 맞추는 것을 고려해서 정규화(normalize) 시킨 결과를 저장한다
- x의 최소치 , y의 최소치를 구해서 뺀다
private List<int[]> normalize(List<int[]> data) {
    if(data == null || data.isEmpty()) return data;
    int minX = Integer.MAX_VALUE;
    int minY = Integer.MAX_VALUE;
    for(int[] d : data) {
        minX = Math.min(minX, d[0]);
        minY = Math.min(minY, d[1]);
    }
    for(int[] d : data) {
        d[0] -= minX;
        d[1] -= minY;
    }
    return data;
}


② game_board의 빈 영역 (List<List<int[]>> emptySpace) 과 table의 블록 영역 (List<List<int[]>> blocks) 을 반복문 수행하며 매칭되는지 확인한다
코드는 심플하다.
- space 영역에 대해 사용하지 않은 block을 회전시키면서 끼워본다
- space 영역에 끼워진다면 result에 블록 사이즈를 누적하고 boolean[] used에 블록 사용 처리를 해준다
private int match(List<List<int[]>> emptySpace, List<List<int[]>> blocks) {
    int result = 0;
    boolean[] used = new boolean[blocks.size()];
    for(int i = 0; i < emptySpace.size(); i++) {
        List<int[]> space = emptySpace.get(i); // 빈 공간
        for(int j = 0; j < blocks.size(); j++) {
            if(used[j]) continue; // 이미 사용한 블록인 경우 넘어간다
            List<int[]> block = blocks.get(j); // 블록
            if(rotateAndCompare(space, block)) {
                result += block.size(); // 블록 크기를 결과에 누적
                used[j] = true; // 사용처리
                break;
            }
        }
    }
    return result;
}
③ block 회전 시키고, 최소x, y와 좌표값에서 빼준다 (shift 처리)
회전 아이디어가 떠오르지 않았는데 chat - gpt가 친철하게 알려주었다

- 절차1. (x, y) -> (y, -x) 로 이동
- 절차2. 최소 x, y를 구해서 회전한 좌표에서 빼준다 (shift)

private boolean rotateAndCompare(List<int[]> space, List<int[]> block) {
    if(space.size() != block.size()) return false; // 사이즈가 틀린 경우
    List<int[]> rotated = block;
    for(int i = 0; i < 4; i++) { // 회전
        if(compare(space, rotated)) {
            return true; // 빈공간에 블록이 맞는 경우
        }
        if(i < 3) {
            rotated = rotate(rotated);
        }
    }
    return false; // 최종적으로 맞지 않는 경우
}
private List<int[]> rotate(List<int[]> block) {
    List<int[]> result = new ArrayList<>();
    int minX = Integer.MAX_VALUE;
    int minY = Integer.MAX_VALUE;
    for(int[] b : block) {
        int x = b[1];
        int y = -b[0];
        result.add(new int[] {x, y});
        if(x < minX) minX = x;
        if(y < minY) minY = y;
    }
    for(int[] r : result) {
        r[0] -= minX;
        r[1] -= minY;
    }
    return result;
}
④ space 영역과 rotate된 block 좌표를 정렬하여 동일한지 확인한다
정렬 람다식을 해석하면
(a, b) -> a[0] == b[0] ? a[1] - b[1] : a[0] - b[0]
x좌표 a[0], b[0]이 같지 않으면 a[0] - b[0] x축 오름차순 정렬한다
x좌표 a[0], b[0]이 같다면 a[1] - b[1] y축 기준 오름차순 정렬한다
private boolean compare(List<int[]> space, List<int[]> block) {
    Collections.sort(space, (a, b) -> a[0] == b[0] ? a[1] - b[1] : a[0] - b[0]); // x가 같다면 y 오름차순
    Collections.sort(block, (a, b) -> a[0] == b[0] ? a[1] - b[1] : a[0] - b[0]);
    for(int i = 0; i < space.size(); i++) {
        int[] s = space.get(i);
        int[] b = block.get(i);
        if(s[0] != b[0] || s[1] != b[1]) {
            return false;
        }
    }
    return true;
}
전체 코드
import java.util.*;
class Solution {
    public int solution(int[][] game_board, int[][] table) {
        List<List<int[]>> emptySpace = new ArrayList<>();
        List<List<int[]>> blocks = new ArrayList<>();
        
        int row = game_board.length;
        int col = game_board[0].length;
        boolean[][] visited = new boolean[row][col];
        for(int i = 0; i < row; i++) {
            for(int j = 0; j < col; j++) {
                if(game_board[i][j] == 0 && !visited[i][j]) {
                    emptySpace.add(extractShape(i, j, row, col, game_board, visited, 0));
                }
            }
        }
        
        row = table.length;
        col = table[0].length;
        visited = new boolean[row][col];
        for(int i = 0; i < row; i++) {
            for(int j = 0; j < col; j++) {
                if(table[i][j] == 1 && !visited[i][j]) {
                    blocks.add(extractShape(i, j, row, col, table, visited, 1));
                }
            }
        }
        
        return match(emptySpace, blocks);
    }
    
    private static int[][] DIR = {
        {0, 1},
        {0, -1},
        {1, 0},
        {-1, 0}
    };
    
    private List<int[]> extractShape(int startX, int startY, int row, int col, int[][] board, boolean[][] visited, int target) {
        List<int[]> result = new ArrayList<>();
        Deque<int[]> que = new ArrayDeque<>();
        que.add(new int[] {startX, startY});
        
        visited[startX][startY] = true;
        
        while(!que.isEmpty()) {
            int[] cur = que.poll();
            result.add(cur);
            
            for(int i = 0; i < 4; i++) {
                int dx = cur[0] + DIR[i][0];
                int dy = cur[1] + DIR[i][1];
                
                if(dx < 0 || dy < 0 || dx >= row || dy >= col) continue;
                if(visited[dx][dy]) continue;
                if(board[dx][dy] != target) continue;
                
                visited[dx][dy] = true;
                que.add(new int[] {dx, dy});
            }
        }
        
        return normalize(result);
    }
    
    
    private List<int[]> normalize(List<int[]> data) {
        if(data == null || data.isEmpty()) return data;
        
        int minX = Integer.MAX_VALUE;
        int minY = Integer.MAX_VALUE;
        
        for(int[] d : data) {
            minX = Math.min(minX, d[0]);
            minY = Math.min(minY, d[1]);
        }
        
        for(int[] d : data) {
            d[0] -= minX;
            d[1] -= minY;
        }
        
        return data;
    }
    
    private int match(List<List<int[]>> emptySpace, List<List<int[]>> blocks) {
        int result = 0;
        boolean[] used = new boolean[blocks.size()];
        
        for(int i = 0; i < emptySpace.size(); i++) {
            List<int[]> space = emptySpace.get(i); // 빈 공간
            
            for(int j = 0; j < blocks.size(); j++) {
                if(used[j]) continue;
                
                List<int[]> block = blocks.get(j); // 블록
                if(rotateAndCompare(space, block)) {
                    result += block.size();
                    used[j] = true;
                    break;
                }
            }
        }
        
        return result;
    }
    
    private boolean rotateAndCompare(List<int[]> space, List<int[]> block) {
        if(space.size() != block.size()) return false; // 사이즈가 틀린 경우
        
        List<int[]> rotated = block;
        for(int i = 0; i < 4; i++) { // 회전
            if(compare(space, rotated)) {
                return true;
            }
            
            if(i < 3) {
                rotated = rotate(rotated);
            }
        }
        
        return false;
    }
    
    private List<int[]> rotate(List<int[]> block) {
        List<int[]> result = new ArrayList<>();
        int minX = Integer.MAX_VALUE;
        int minY = Integer.MAX_VALUE;
        for(int[] b : block) {
            int x = b[1];
            int y = -b[0];
            
            result.add(new int[] {x, y});
            
            if(x < minX) minX = x;
            if(y < minY) minY = y;
        }
        
        for(int[] r : result) {
            r[0] -= minX;
            r[1] -= minY;
        }
        
        return result;
    }
    
    
    private boolean compare(List<int[]> space, List<int[]> block) {
        Collections.sort(space, (a, b) -> a[0] == b[0] ? a[1] - b[1] : a[0] - b[0]); // x가 같다면 y 오름차순
        Collections.sort(block, (a, b) -> a[0] == b[0] ? a[1] - b[1] : a[0] - b[0]);
        
        for(int i = 0; i < space.size(); i++) {
            int[] s = space.get(i);
            int[] b = block.get(i);
            if(s[0] != b[0] || s[1] != b[1]) {
                return false;
            }
        }
        
        return true;
    }
}
'알고리즘 > 그래프 탐색' 카테고리의 다른 글
| [BOJ16236] 아기 상어 (Java, BFS, 시뮬레이션) (0) | 2025.01.06 | 
|---|---|
| [프로그래머스] 아이템 줍기 (BFS, Java, lv3) (2) | 2024.07.18 | 
| [BOJ2573] 빙산 (DFS, BFS, 그래프 탐색) (1) | 2023.09.21 | 
| [BOJ 2636] 치즈 (Java, BFS) (0) | 2023.06.15 | 
| [BOJ 3190] 뱀 (Java, BFS, 구현) (0) | 2023.06.14 | 

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!