[완전탐색] 게임판 덮기(문제 ID:BOARDCOVER, 난이도:하) 본문
반응형
    
    
    
  1. 문제정의

 H*W 크기의 게임판이 있습니다. 게임판은 검은 칸과 흰 칸으로 구성된 격자 모양을 하고 있는데 이 중 모든 흰 칸을 3칸짜리 L자 모양의 블록으로 덮고 싶습니다. 이 때 블록들은 자유롭게 회전해서 놓을 수 있지만, 서로 겹치거나, 검은 칸을 덮거나, 게임판 밖으로 나가서는 안 됩니다. 위 그림은 한 게임판과 이를 덮는 방법을 보여줍니다.
게임판이 주어질 때 이를 덮는 방법의 수를 계산하는 프로그램을 작성하세요.
입력
력의 첫 줄에는 테스트 케이스의 수 C (C <= 30) 가 주어집니다. 각 테스트 케이스의 첫 줄에는 2개의 정수 H, W (1 <= H,W <= 20) 가 주어집니다. 다음 H 줄에 각 W 글자로 게임판의 모양이 주어집니다. # 은 검은 칸, . 는 흰 칸을 나타냅니다. 입력에 주어지는 게임판에 있는 흰 칸의 수는 50 을 넘지 않습니다.
출력
한 줄에 하나씩 흰 칸을 모두 덮는 방법의 수를 출력합니다.
2. 문제 설계
완전 탐색을 이용해 모든 경우의 수를 알아보는 것입니다. 재귀 호출을 이용해 코드를 만들어 봅시다.
부분 문제 정의
항상 빈 칸 중에서 가장 위, 그 중에서도 가장 왼쪽에 칸을 처음으로 채운다고 가정합니다. 채우는 방법은 4가지 유형입니다. 각 부분문제가 성공적으로 채워질때마다 +1이 될 것이고 결과적으로 경우의 수를 모두 더한 수가 됩니다.
3. 구현
#include <iostream>
#include<vector>
using namespace std;
//타일 선택 : 가장 위쪽 그리고 왼쪽이 기준
//덮는 방법 : 4가지 x 3칸 x (y, x)
const int coverType[4][3][2] = {
        {{0,0}, {1, 0}, {0, 1}}, //「
        {{0, 0}, {0, 1}, {1, 1}}, //ㄱ
        {{0, 0}, {1, 0}, {1, 1}}, //ㄴ
        {{0, 0}, {1, 0}, {1, -1}} //」
};
//board의 (y, x)를 type번 방법으로 덮거나, 덮었던 블록을 없앤다
//delta가 1이면 덮고, -1이면 덮었던 블록을 없앤다
//게임판 밖으로 나가거나, 겹치거나, 검은 칸을 덮을 때 false 반환
bool set(vector<vector<int>>& board, int y, int x, int type, int delta){
        bool ok = true;
        for (int i = 0; i < 3; i++){
               const int ny = y + coverType[type][i][0];
               const int nx = x + coverType[type][i][1];
               if (ny < 0 || ny >= (int)board.size() || nx < 0 
    || nx >= (int)board[0].size()) //범위를 초과할 경우
                       ok = false;
               else if ((board[ny][nx] += delta) > 1) //겹쳐질 경우
                       ok = false;
        }
        return ok;
}
//board의 모든 빈 칸을 덮을 수 있는 방법의 수를 반환
//board[i][j]=1 이미 덮인 칸 혹은 검은 칸
//board[i][j]=0 아직 덮이지 않은 칸
int cover(vector<vector<int>> &board){
        //아직 채우지 못한 칸 중 가장 윗줄 왼쪽에 있는 칸을 찾는다
        int y = -1, x = -1;
        for (int i = 0; i < (int)board.size(); i++){
               for (int j = 0; j < (int)board[i].size(); j++)
                       if (board[i][j] == 0) {//아직 채우진 못한 칸 찾음
                              y = i;
                              x = j;
                              break;
                       }
               if (y != -1) break;
        }
        //기저 사례: 모든 칸을 채웠으면 1을 반환
        if (y == -1) return 1;
        int ret = 0;
        for (int type = 0; type < 4; type++){
               //만약 board[y][x]를 type 형태로 덮을 수 있으면 재귀 호출
               if (set(board, y, x, type, 1))
                    ret += cover(board);
               //덮었던 블록 치운다
               set(board, y, x, type, -1);
        }
        return ret;
}
int main(void){
        int testCase, H, W;
        char bw[20]; 
        cin >> testCase;
        for (int i = 0; i < testCase; i++){
               cin >> H >> W;
    vector<vector<int> > board(H, vector<int>(W, 0));
               for (int i = 0; i < H; i++) {
                       cin >> bw;
                       for (int j = 0; j < W; j++)
                               board[i][j] = (bw[j] == '#') ? 1 : 0;
               }
               cout <<"OUTPUT : "<<cover(board) << endl;
        }
        return 0;
}
4. 테스트

반응형
    
    
    
  '알고리듬' 카테고리의 다른 글
| [그래프] 최소 신장 트리(MST, minimum spanning tree) - 1 (0) | 2021.06.09 | 
|---|---|
| [완전탐색] 시계 맞추기(문제 ID:CLOCKSYNC, 난이도:중) (0) | 2021.06.04 | 
| [완전탐색] 문제:소풍(문제 ID: PICNIC, 난이도: 하) (0) | 2021.06.03 | 
| [탐욕법] 거스름돈(난이도:하) (0) | 2021.06.02 | 
| [트리] 트리(Tree) 순회 (0) | 2021.06.01 | 
			  Comments