본문 바로가기
Study/알고리즘 문제해결전략(종만북)

[종만북] BOARDCOVER - 게임판 덮기 (자바 java)

by Nahwasa 2023. 3. 20.

알고리즘 문제해결전략(종만북) 스터디 메인 페이지

문제 : aoj-BOARDCOVER

 

 

풀이

  모든 칸에 블록을 놓아본다고 하자. 이 경우 흰 칸의 수는 50을 넘지 않는다고 했고, 블록 하나에 3칸씩이므로 최대 16개를 덮으면 된다. 블록의 모양은 4가지이므로, O(C x 4^16) 이 필요하다. 방법을 좀 다르게 생각해서 각 칸마다 놓거나 안놓거나 해봐도 O(C x 2^50)이므로 마찬가지로 불가능하다.

 

  여기서 생각해볼 점은, 블록의 3칸 중 가장 상단 좌측의 위치를 기준으로 놓는다고 생각해보자.

 

  그리고 주어진 게임판의 상단부터 하단으로, 좌측에서 우측으로 쭉 보면서 블록을 놓는다고 해보자. 이렇게 되면 만약 블록을 놓지 않고 그냥 지나쳤을 때, 해당 위치엔 다시는 블록을 놓을 수 없음이 증명된다. 예를들어 3x3 게임판에 첫번째 칸을 선택하지 않고, 2번째 칸부터 놓는다고 할 때 위의 규칙대로라면 첫 번째 칸은 이후 다시는 채울 방법 자체가 없다. 따라서 차례대로 보면서 빈칸이라면 무조건 4개의 모양 중 하나를 선택해야 한다.

 

  이 경우 빈 칸이 생기더라도 L자 모양으로 3칸이 주변에 없다면 거기서 루프가 종료되므로, 시간복잡도를 수학적으로 계산하긴 힘들지만 모양이 안맞는 경우들이 생각보다 많아서 생각보다 횟수가 크게 늘어나지 않는다. 예를들어 최악의 경우라 생각되는 아래의 경우 몇 번 재귀 함수가 호출되는지 확인해봤다.

1
6 8
........
........
........
........
........
........

위의 경우 coverBoard 함수는 68,656 번밖에 호출되지 않았다.

 

 

코드 : github

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.StringTokenizer;

public class Main {

    private static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    private static final int DR[][] = {{1, 1, 0, 0}, {1, 1, 1, 1}};
    private static final int DC[][] = {{0, 0, 1, 1}, {-1, 1, 1, 0}};
    private boolean[][] arr = new boolean[22][22];
    int r, c;


    public static void main(String[] args) throws Exception {
        Main main = new Main();
        int c = Integer.parseInt(br.readLine());
        StringBuilder sb = new StringBuilder();
        while (c-->0) {
            sb.append(main.solution()).append('\n');
        }
        System.out.print(sb);
    }

    private int solution() throws Exception {
        StringTokenizer st = new StringTokenizer(br.readLine());
        r = Integer.parseInt(st.nextToken());
        c = Integer.parseInt(st.nextToken());

        int remain = setupBoard();
        if (!isRemainModBy3(remain)) {
            return 0;
        }
        return coverBoard(remain / 3, 0);
    }

    private int setupBoard() throws IOException {
        int remain = 0;
        for (int i = 0; i < arr.length; i++) {
            Arrays.fill(arr[i], true);
        }
        for (int i = 1; i <= r; i++) {
            String s = br.readLine();
            for (int j = 1; j <= c; j++) {
                arr[i][j] = s.charAt(j-1) == '#' ? true : false;
                if (!arr[i][j])
                    remain++;
            }
        }
        return remain;
    }

    private boolean isRemainModBy3(int remain) {
        return remain%3 == 0;
    }

    private int coverBoard(int remain, int sr) {
        if (remain == 0) {
            return 1;
        }

        for (int i = sr; i <= r; i++) {
            for (int j = 0; j <= c; j++) {
                if (arr[i][j]) continue;

                int cnt = 0;
                for (int dir = 0; dir < 4; dir++) {
                    int nr1 = i+ DR[0][dir];
                    int nc1 = j+ DC[0][dir];
                    int nr2 = i+ DR[1][dir];
                    int nc2 = j+ DC[1][dir];
                    if (arr[nr1][nc1]||arr[nr2][nc2]) continue;

                    arr[nr1][nc1] = true;
                    arr[nr2][nc2] = true;
                    arr[i][j] = true;
                    cnt += coverBoard(remain-1, i);
                    arr[nr1][nc1] = false;
                    arr[nr2][nc2] = false;
                    arr[i][j] = false;
                }
                return cnt;

            }
        }
        return 0;
    }
}

 

 

※ 종만북에 이미 풀이가 있는데 제 풀이를 올리는 이유는 제가 책의 풀이를 보지 않고 문제를 푼 후 제 풀이를 올리고 나서 책의 풀이를 보는 방식으로 풀어보고 싶기 때문입니다.

댓글