티스토리 뷰
#. 문제
* 이 문제의 저작권은 Algospot에 있습니다.
H*W 크기의 게임판이 있습니다.
게임판은 검은 칸과 흰 칸으로 구성된 격자 모양을 하고 있는데 이 중 모든 흰 칸을 3칸짜리 L자 모양의 블록으로 덮고 싶습니다. 이 때 블록들은 자유롭게 회전해서 놓을 수 있지만, 서로 겹치거나, 검은 칸을 덮거나, 게임판 밖으로 나가서는 안 됩니다. 위 그림은 한 게임판과 이를 덮는 방법을 보여줍니다.
게임판이 주어질 때 이를 덮는 방법의 수를 계산하는 프로그램을 작성하세요.
[ 입력 ]
력의 첫 줄에는 테스트 케이스의 수 C (C <= 30) 가 주어집니다. 각 테스트 케이스의 첫 줄에는 2개의 정수 H, W (1 <= H,W <= 20) 가 주어집니다. 다음 H 줄에 각 W 글자로 게임판의 모양이 주어집니다. # 은 검은 칸, . 는 흰 칸을 나타냅니다. 입력에 주어지는 게임판에 있는 흰 칸의 수는 50 을 넘지 않습니다.
[ 출력 ]
한 줄에 하나씩 흰 칸을 모두 덮는 방법의 수를 출력합니다.
#. 문제 해결 과정
1. 문제를 읽고 이해하기
2. 문제를 익숙한 용어로 재정의 + 추상화
- 게임판의 흰 칸을 모두 3칸짜리 L자 모양의 블록으로 덮어야 함
- 블록들을 자유롭게 회전 가능하지만, 겹치거나, 검은 칸을 덮거나, 게임판 밖으로 나가면 안됨
- 게임판이 주어지면 이를 덮는 방법의 수를 계산하여 출력
- 조건
ㄴ 테스트 케이스의 수 C (C <= 30)
ㄴ 각 테스트 케이스의 첫 줄에는 2개의 정수 H, W (1 <= H,W <= 20)
ㄴ 입력에 주어지는 게임판에 있는 흰 칸의 수는 50 을 넘지 않음
3. 어떻게 해결할지 계획 세우기(알고리즘, 자료구조 선택) -> 풀이
4. 계획 검증하기(수행 시간과 사용 메모리 확인)
- 수행 시간 : 2초 이내 / 사용 메모리 : 64MB 이하
- 'ㄱ'자 한 블록을 놓을 때마다 네 가지의 선택지, 최대 50/3(16)개의 블록을 놓기 때문에 가능한 답의 상한은 4^16 => 2^32
5. 계획 수행하기
6. 어떻게 풀었는지 돌아보고, 개선할 방법이 있는지 찾아보기
#. 풀이
1. 테스트 케이스의 수 C (C <= 30) 입력
2. 2개의 정수 H, W (1 <= H,W <= 20) 입력
3. 배열에 게임판의 블록들을 저장
- 흰 칸이 50을 초과하면 에러
- 흰 칸이 3의 배수가 아닌 경우 게임판을 덮을 수 없으므로 0 return (기저 사례)
4. 게임판의 빈 칸을 찾아 set() 함수에 넘겨주는 cover() 함수
- 중복의 경우를 제외하기 위해 각 단계마다 빈 킨의 가장 윗, 가장 왼쪽 칸을 먼저 덮도록 실행 (기준)
- 첫 번째 빈 칸을 찾은 후 'ㄱ'자 블록을 덮을 방법을 하나하나 시도 -> set() 함수로 재귀 호출
- 'ㄱ'자 블록이 놓였다면 남은 게임판을 재귀 호출에 넘겨 경우의 수를 계산
5. 블록을 높는 set() 함수
- 블록이 놓였다면 true 반환
- 블록이 겹치거나, 검은 칸을 덮거나, 게임판을 나가는 경우 false 반환
#. 코드
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 | import java.io.*; import java.util.StringTokenizer; public class C66_BOARDCOVER { // 'ㄱ'자 블록을 놓을 수 있는 상대적 위치 public static int[][][] blockType = { {{0,0}, {0,1}, {1,0}}, {{0,0}, {0,1}, {1,1}}, {{0,0}, {1,0}, {1,1}}, {{0,0}, {1,0}, {1,-1}}, }; public static int[][] board; public static int C, H, W; public static boolean set(int y, int x, int type, int act) { boolean check = true; // 'ㄱ'자 블록을 board에 놓아보기 for(int i=0; i<3; i++) { int dy = y + blockType[type][i][0]; int dx = x + blockType[type][i][1]; if(dy<0 || dy>=H || dx<0 || dx>=W) // 블록이 게임판으로 나갈 경우 check = false; else if((board[dy][dx] += act) > 1) // 블록이 겹치는 경우 & 블록 치우기 check = false; } return check; } /* 4. board의 모든 빈 칸을 덮을 수 있는 방법의 수를 반환하는 함수 */ public static int cover() { // 채우지 못 한 가장 위&왼쪽 에 있는 칸 탐색 int y = -1, x = -1; for(int hg=0; hg<H; hg++) { for(int wd=0; wd<W; wd++) { if(board[hg][wd] == 0) { y = hg; x = wd; break; } } // 빈 칸을 찾았을 경우 반복문에서 탈출 if(y != -1) break; } // 모든 칸을 채웠을 경우 1 반환 if(y == -1) return 1; int ret = 0; // 가장 위&왼쪽에 있는 빈 칸에 'ㄱ'블록을 덮을 4가지 방법을 시도 for(int type=0; type<4; type++) { // board[y][x]를 해당 type으로 덮을 수 있다면 재귀 호출 if(set(y, x, type, 1)) ret += cover(); // 덮을 수 없다면 덮었던 블록을 치우기 set(y, x, type, -1); } return ret; } public static void Boardcover() throws IOException { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); /* 1. 테스트 케이스 입력 */ C = Integer.parseInt(br.readLine().trim()); assert C <= 30 : "Error :: C(testCase) <= 30"; for(int tc=0; tc<C; tc++) { /* 2. H(Height)과 W(Width) 입력 */ StringTokenizer st = new StringTokenizer(br.readLine().trim()); H = Integer.parseInt(st.nextToken()); // Height W = Integer.parseInt(st.nextToken()); // Width assert 1 <= H || 1 <= W || H <= 20 || W <=20 : "Error :: 1 <= H,W <= 20"; /* 3. board에 블럭을 저장 */ board = new int[H][W]; int countWhiteBlock = 0; for(int hg=0; hg<H; hg++) { String tempString = br.readLine(); for(int wd=0; wd<W; wd++) { // 잘못된 블럭이 입력될 경우 예외 처리 assert tempString.charAt(wd)=='#' || tempString.charAt(wd)=='.' : "Error :: block is available only int # or ."; if (tempString.charAt(wd)=='#') board[hg][wd] = 1; else countWhiteBlock++; } } // 데이터가 잘 들어갔는지 출력해서 확인 // for(int hg=0; hg<H; hg++) { // for(int wd=0; wd<W; wd++) { // bw.write(String.format("%d",board[hg][wd])); // } // bw.write("\n"); // } // bw.write("\n"); assert countWhiteBlock <= 50 : // 흰 칸이 50칸을 초과하면 에러 발생 "Error :: countWhiteBlock <= 50"; // 흰 칸이 3의 배수가 아닐 경우 0 출력 if(countWhiteBlock%3 != 0) bw.write(String.format("%d\n", 0)); // 모든 칸이 이미 다 채워져있는 경우 else if(countWhiteBlock == 0) bw.write(String.format("%d\n", 1)); else bw.write(String.format("%d\n", cover())); } bw.close(); } public static void main(String[] args) throws IOException { C66_BOARDCOVER.Boardcover(); } } | cs |
#. 결과
- Input --------------------------------------------------------
3
3 7
#.....#
#.....#
##...##
3 7
#.....#
#.....#
##..###
8 10
##########
#........#
#........#
#........#
#........#
#........#
#........#
##########
------------------------------------------------------------------
- Output --------------------------------------------------------
0
2
1514
------------------------------------------------------------------
'PS > Problem_Solving' 카테고리의 다른 글
[Algospot] QUADTREE (분할 정복) (0) | 2019.09.15 |
---|---|
[Algospot] CLOCKSYNC (최적화) (0) | 2019.09.07 |
[Algospot] PICNIC (경우의 수, 탐색) (0) | 2019.09.04 |
[Algospot] BOGGLE(재귀 호출, 동적계획법) (0) | 2019.09.03 |
[SWEA] 1240. 단순 2진 암호코드(JAVA) (0) | 2019.08.10 |