※ 웹 환경에 최적화된 서식이므로 웹 페이지로 열람함을 권장.
게임판 덮기(링크)
빈 곳이 있는 게임판이 주어질 때 L 모양의 블럭을 한 개씩 덮어 모든 칸을 채우는 방법의 가짓수를 계산한다.
재귀적으로 생각하기
게임판에 L 모양의 블럭을 한 개 덮는 것을 하나의 작업 조각으로 생각한다. 재귀 함수는 빈 곳이 있는 게임판이 주어질 때 4 가지 모양의 L 블럭을 각각 덮을 수 있는지 검사해보는 작업을 반복한다.
중복으로 세는 문제
블록을 놓는 순서에 상관없이 같은 블록들로 덮었다면 한 가지 경우이다. 블록을 놓는 순서를 강제하면 놓는 순서에 따라 별개의 경우로 세는 문제를 막을 수 있다. 가장 왼쪽 위의 빈 칸부터 덮어 나가는 것으로 순서를 강제하자.
무식하게 풀 수 있을까?
빈 칸의 수는 50개를 넘지 않으므로 최대 16개 (L 블록 한개는 3칸) 의 블록을 놓는다. 따라서 가능한 답의 상한은 4^16 개가 된다. 완전 탐색으로는 버거운 수의 상한이지만 실제로 각 단계에서 선택 가능한 블록 배치는 크게 제한된다. 예를 들어 빈 칸이 6개 있을 때 이론 상 4^2 가지의 방법이 있지만 실제로는 2가지 방법으로만 배치할 수 있다.
#include <iostream>
#include <vector>
using namespace std;
// L blocks
/*
# ## ## #
## # # ##
*/
const int LBlock[4][3][2] = {
{{0,0},{1,0},{1,1}},
{{0,0},{0,1},{1,0}},
{{0,0},{0,1},{1,1}},
{{0,0},{1,0},{1,-1}}
};
int H, W;
int boardcover(vector<vector<char>>& board) {
// 가장 왼쪽 상단에 위치한 빈자리를 찾는다.
int firstY = -1, firstX = -1;
for (int y = 0; y < H; ++y) {
for (int x = 0; x < W; ++x) {
if (board[y][x] == '.') {
firstY = y;
firstX = x;
break;
}
}
if (firstY != -1) break;
}
// 모두 덮힌 한 가지 경우의 의미로 1 반환.
if (firstY == -1) return 1;
int ret = 0;
for (int i = 0; i < 4; ++i) {
bool isCoverable = true;
for (int j = 0; j < 3; ++j) {
// (y[j], x[j]): coordinate for L blocks.
int y[j] = firstY + LBlock[i][j][0];
int x[j] = firstX + LBlock[i][j][1];
if (y[j] < 0 || H <= y[j] || x[j] < 0 || W <= x[j])
isCoverable = false;
else if (board[y[j]][x[j]] == '#')
isCoverable = false;
}
// 현재 블록은 엎을 수 없으므로 다음 블록을 검사.
if (isCoverable == false) continue;
for (int j = 0; j < 3; ++j)
board[y[j]][x[j]] = '#';
ret += boardcover(board);
for (int j = 0; j < 3; ++j)
board[y[j]][x[j]] = '.';
}
return ret;
}
int main() {
int C; cin >> C;
vector<vector<char>> board;
vector<int> answers;
while (C-- > 0) {
cin >> H >> W;
vector<char> row;
for (int i = 0; i < H; ++i) {
for (int j = 0; j < W; ++j) {
char c; cin >> c;
row.push_back(c);
}
board.push_back(row);
row.clear();
}
answers.push_back(boardcover(board));
board.clear();
}
for (auto e: answers) cout << e << " ";
return 0;
}
'문제 해결 알고리즘 기초 > 완전 탐색' 카테고리의 다른 글
[알고스팟] 시계 맞추기 - 완전 탐색, 재귀 (0) | 2020.08.20 |
---|---|
[알고스팟] 여행하는 외판원 문제 - 완전 탐색, 재귀 (0) | 2020.08.04 |
[알고스팟] 소풍 - 완전 탐색, 재귀 (0) | 2020.07.30 |
[알고스팟] 보글 게임 - 완전 탐색, 재귀 (0) | 2020.07.21 |
완전 탐색으로 n 개 중 r 개 고르기 (조합) (0) | 2020.07.21 |