[프로그래머스] 84021 퍼즐 조각 채우기 - JAVA

2025. 7. 6. 17:16·알고리즘/문제풀이

[프로그래머스] 84021 퍼즐 조각 채우기 - JAVA


문제

https://school.programmers.co.kr/learn/courses/30/lessons/84021

 

문제 분석

조건

`game_board`에 있는 빈 공간(0)에 `table`에 있는 퍼즐 조각(1)을 아래 조건들에 맞춰 채워 넣는 문제이다. 목표는 최대한 많은 칸을 채우는 것이다.

1. 퍼즐은 빈 공간에 딱 맞는 곳에만 넣을 수 있다.(1개의 퍼즐을 넣은 뒤 인접한 곳에 빈 칸이 있으면 안된다.)
2. 퍼즐은 회전은 가능하나 뒤집을 수는 없다.
3. 한번 넣은 퍼즐은 다시 사용할 수 없다.

풀이방법

빈 공간의 모양과 해당 빈 공간이 채워졌는지를 정보로 가진 `EmptyArea` 클래스를 정의하여 이용했다. `Block` 클래스는 도형의 크기와 정규화된 모양을 저장하며, 회전을 처리하는 함수를 갖도록 했다.

먼저 `game_board`를 기준으로 빈칸들을 모두 찾아 정리했다. 그 후 `table`을 탐색하며 블록을 발견할 때마다, 같은 크기의 빈칸들 중에서 같은 모양이 있는지 확인하고, 없다면 퍼즐을 회전한 뒤 다시 확인하도록 구현하였다.

이 문제의 핵심은 도형을 '정규화'하는 과정이라고 생각한다. BFS인 `createBlock` 함수에서 블록을 이루는 좌표를 모두 찾은 뒤, 이 좌표들을 감싸는 가장 작은 사각형의 가장 왼쪽 위 좌표를 (0,0)으로 삼아 모든 좌표를 평행 이동시켰다. 이를 통해, 맵의 어느 위치에 있든 모양만 같다면 항상 동일한 형태로 표현되도록 기준을 통일했다.

코드

import java.util.*;

class Block {
    int size;
    boolean[][] shape;

    public Block(int size, boolean[][] shape) {
        this.size = size;
        this.shape = shape;
    }

    // 모양 회전
    public void rotate() {
        int rows = shape.length;
        int cols = shape[0].length;
        boolean[][] newShape = new boolean[cols][rows];
        for (int r = 0; r < rows; r++) {
            for (int c = 0; c < cols; c++) {
                newShape[c][rows - 1 - r] = shape[r][c];
            }
        }
        shape = newShape;
    }
}

class EmptyArea {
    Block block;
    boolean fill;

    public EmptyArea(Block block) {
        this.block = block;
        this.fill = false;
    }
}

class Solution {
    static int[][] dirs = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};

    public int solution(int[][] game_board, int[][] table) {
        int answer = 0;
        List<EmptyArea>[] areaList = new List[7];   // 빈 구역 리스트
        for (int i = 1; i < 7; i++) {
            areaList[i] = new ArrayList<>();
        }

        // 빈 공간 찾아서 정리
        for (int r = 0; r < game_board.length; r++) {
            for (int c = 0; c < game_board.length; c++) {
                if (game_board[r][c] == 0) {
                    Block block = createBlock(game_board, r, c, 0);
                    areaList[block.size].add(new EmptyArea(block));
                }
            }
        }

        for (int r = 0; r < table.length; r++) {
            for (int c = 0; c < table.length; c++) {
                if (table[r][c] == 1) {
                    // 블럭을 찾음(모양과 크기 나옴)
                    Block block = createBlock(table, r, c, 1);
                    boolean fill = false;
                    for (int i = 0; i < 4; i++) {
                        // 해당 사이즈의 남아있는 빈칸들에 넣을 수 있는지 확인
                        for (EmptyArea emptyArea : areaList[block.size]) {
                            if (emptyArea.fill) {
                                // 이미 채워진 빈칸
                                continue;
                            }
                            // 깊은 비교(2차원 배열 비교)
                            if (Arrays.deepEquals(emptyArea.block.shape, block.shape)) {
                                emptyArea.fill = true;
                                answer += block.size;
                                fill = true;
                                break;
                            }
                        }
                        if (fill) {
                            break;
                        }
                        block.rotate(); // 칸에 못 넣었을 경우 회전

                    }
                }
            }
        }
        return answer;
    }

    /**
     * 블럭/빈 공간 찾아서 Block 클래스로 반환
     *
     * @param find // 찾아야하는 숫자(0: 빈 공간, 1: 블럭)
     * @return
     */
    private Block createBlock(int[][] board, int sr, int sc, int find) {
        int turn = find == 1 ? 0 : 1;   // 방문지점 체크를 위한 변수
        // 모양 배열의 크기를 위한 변수
        int minR = 51;
        int minC = 51;
        int maxR = -1;
        int maxC = -1;
        // 모양을 이루는 좌표들 배열
        int[][] posArr = new int[7][2];
        int size = 0;
        Queue<int[]> queue = new ArrayDeque<>();
        board[sr][sc] = turn;
        queue.add(new int[]{sr, sc});
        while (!queue.isEmpty()) {
            int[] now = queue.poll();
            minR = Math.min(minR, now[0]);
            minC = Math.min(minC, now[1]);
            maxR = Math.max(maxR, now[0]);
            maxC = Math.max(maxC, now[1]);
            posArr[size++] = new int[]{now[0], now[1]};
            for (int[] dir : dirs) {
                int r = now[0] + dir[0];
                int c = now[1] + dir[1];
                if (!isInvalid(r, c, board.length)) {
                    if (board[r][c] == find) {
                        queue.add(new int[]{r, c});
                        board[r][c] = turn;
                    }
                }
            }
        }

        // 모양. 블럭을 구성하는 좌표들 중에서 가장 왼쪽 위에 있는 칸을 (0, 0)으로 만듦
        boolean[][] shape = new boolean[maxR - minR + 1][maxC - minC + 1];
        for (int i = 0; i < size; i++) {
            posArr[i][0] -= minR;
            posArr[i][1] -= minC;
            shape[posArr[i][0]][posArr[i][1]] = true;
        }
        return new Block(size, shape);
    }

    /**
     * 범위 확인(구역 밖이면 true)
     */
    private boolean isInvalid(int r, int c, int length) {
        return r < 0 || c < 0 || r >= length || c >= length;
    }
}

 

728x90
저작자표시 비영리 변경금지 (새창열림)
'알고리즘/문제풀이' 카테고리의 다른 글
  • [백준/BOJ] 2473 세 용액 - JAVA
  • [백준/BOJ] 21610 마법사 상어와 비바라기 - JAVA
  • [백준/BOJ] 1202 보석 도둑- JAVA
  • [프로그래머스] 12907 거스름돈 - JAVA
LIRI
LIRI
  • LIRI
    기록
    LIRI
  • 전체
    오늘
    어제
    • 분류 전체보기 (73)
      • 블로그 꾸미기 (0)
      • Spring (6)
      • React (3)
      • CS (0)
      • 알고리즘 (57)
        • 개념 (2)
        • 문제풀이 (54)
      • Java (1)
      • DB (1)
      • log (4)
        • SSAFY (3)
        • 궁금 (1)
  • 블로그 메뉴

    • 홈
    • 방명록
  • 공지사항

  • 인기 글

  • 태그

    SSAFY
    pccp모의고사
    dp
    알고리즘
    Java
    BOJ
    Springsecurity
    싸피
    너비우선탐색
    불 끄기
    알고리즘 문제풀이
    SSAFY 9기
    JWT
    리액트
    ssafy 합격 후기
    그리디
    Spring
    최장증가부분수열
    비트마스킹
    LIS
    springboot
    dfs
    골드1
    도대체왜
    백준
    BFS
    Security
    프로그래머스
    LV2
    lv3
  • 최근 댓글

  • 최근 글

  • 250x250
  • hELLO· Designed By정상우.v4.10.3
LIRI
[프로그래머스] 84021 퍼즐 조각 채우기 - JAVA
상단으로

티스토리툴바