컴퓨터기본/문제풀이

[백준] 16985번: Maaaaaaaaaze

차가운오미자 2021. 11. 18. 20:43

https://www.acmicpc.net/problem/16985

 

16985번: Maaaaaaaaaze

첫째 줄부터 25줄에 걸쳐 판이 주어진다. 각 판은 5줄에 걸쳐 주어지며 각 줄에는 5개의 숫자가 빈칸을 사이에 두고 주어진다. 0은 참가자가 들어갈 수 없는 칸, 1은 참가자가 들어갈 수 있는 칸을

www.acmicpc.net

문제 이해

문제가 매우 길고 어려워 보이지만 결국 DFS를 통해 모든 경우의 수를 탐색하는 문제이다.

5*5 판을 3번 회전시켜 총 4가지의 경우의 수가 있다. 

이런 판이 5개가 있는데, 이걸 순서를 바꿔가며 쌓을 수 있다.

그리고 이렇게 쌓은 5*5*5의 정육면체에서 한 꼭짓점에서 마주보는 꼭짓점으로 가는 경로의 쌍이 4개가 존재한다 (정육면체 모서리는 8개니까)

즉, 총 5*5*4의 경우의 수를 모두 해봐서, 존재하는 경로의 최단 거리를 구하면 된다.

 

즉,

  • DFS의 depth는 5로, 각 depth 별로 판을 하나 선택하고
  • 각 판을 4번 회전시키고
  • depth == 5가 되었을 때, (정육면체 완선) 4개의 꼭짓점에 BFS를 돌린다

DFS나 BFS는 딱히 어려운 부분 없이 정석적으로 코드를 작성하면 되기 때문에 큰 문제가 없다.

 

작성 코드

#include <iostream>
#include <cstring>
#define INF 0x7fffffff
using namespace std;

int inputMap[5][5][5];
int map[5][5][5];
int visited[5][5][5];

int used[5]; // 판 이용
int tmpmap[5][5];
int ans = 0x7fffffff;

typedef struct p {
    int h, y, x, t;
}POINT;
POINT q[5 * 5 * 5 * 10];
int wp, rp;
void enq(int h, int y, int x, int t) { q[wp].h = h, q[wp].y = y, q[wp].x = x, q[wp++].t = t; }
POINT deq() { return q[rp++]; }
int empty() { return wp == rp; }

int dh[] = { 0, 0, 0, 0, -1, 1 }; // 동서남북상하
int dy[] = { 0, 0, 1, -1, 0, 0 };
int dx[] = { 1, -1, 0, 0, 0, 0 };

void Get_Input(void) {
    for (int i = 0; i < 5; i++) {
        for (int j = 0; j < 5; j++) {
            for (int k = 0; k < 5; k++) {
                cin >> inputMap[i][j][k];
            }
        }
    }
}

void BFS(int sh, int sy, int sx, int eh, int ey, int ex) {

    if (map[sh][sy][sx] == 0 || map[eh][ey][ex] == 0) return;

    wp = rp = 0;
    memset(visited, 0, sizeof(visited));

    enq(sh, sy, sx, 0);
    visited[sh][sy][sx] = 1;
    while (!empty()) {
        POINT cur = deq();
        if (cur.h == eh && cur.y == ey && cur.x == ex) {
            if (cur.t < ans) ans = cur.t;
            return;
        }
        for (int i = 0; i < 6; i++) {
            int nh = dh[i] + cur.h;
            int ny = dy[i] + cur.y;
            int nx = dx[i] + cur.x;
            if (nh < 0 || nh >= 5 || ny < 0 || ny >= 5 || nx < 0 || nx >= 5) continue;
            if (visited[nh][ny][nx] == 1) continue;
            if (map[nh][ny][nx] == 0) continue;
            enq(nh, ny, nx, cur.t + 1);
            visited[nh][ny][nx] = 1;
        }
    }
}

void Print_Map(int idx) {
    for (int i = 0; i < 5; i++) {
        for (int j = 0; j < 5; j++) {
            cout << inputMap[idx][i][j];
        }
        cout << "\n";
    }
    cout << "\n";
}


void DFS(int cnt) {
    // cnt: 지금까지 쌓은 거
    // 종료
    int ret;
    if (cnt == 5) {
        // 꼭짓점 선택해서 BFS -> 총 4쌍
        BFS(0, 0, 0, 4, 4, 4);
        BFS(0, 4, 0, 4, 0, 4);
        BFS(0, 0, 4, 4, 4, 0);
        BFS(0, 4, 4, 4, 0, 0);
        return;
    }

    // 재귀
    // 0. 선택하기
    for (int i = 0; i < 5; i++) {
        if (used[i] == 1) continue;
        used[i] = 1;
        for (int l = 0; l < 4; l++) {
            // 1. 맵에 올리기
            for (int j = 0; j < 5; j++) {
                for (int k = 0; k < 5; k++) {
                    map[cnt][j][k] = inputMap[i][j][k];
                }
            }
            // 2. 다음 맵 선택을 위한 DFS
            DFS(cnt + 1);
            // 3. 돌리기
            memset(tmpmap, 0, sizeof(tmpmap));
            for (int j = 0; j < 5; j++) {
                for (int k = 0; k < 5; k++) {
                    tmpmap[k][4 - j] = inputMap[i][j][k];
                }
            }
            memcpy(inputMap[i], tmpmap, sizeof(tmpmap));
        }
        used[i] = 0;
    }
}

int main(void) {
    freopen("in.txt", "r", stdin);
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);
    cout.tie(NULL);
    Get_Input();
    DFS(0);
    if (ans == INF) cout << "-1";
    else cout << ans;
    return 0;
}

 

 

'컴퓨터기본 > 문제풀이' 카테고리의 다른 글

[백준] 17779번: 게리맨더링 2  (0) 2021.11.19
[백준] 17281번: ⚾  (0) 2021.11.18
[백준] 11003번: 최솟값 찾기  (0) 2021.11.18
[백준] 7569번: 토마토  (0) 2021.11.18
[백준] 2933번: 미네랄  (0) 2021.11.18