본문 바로가기

프로젝트

알고리즘 - 프로젝트 1 - 레이튼 교수와 이상한 마을 135

유튜브를 보다가 오랜만에 레이튼 교수 시리즈를 다시 접하게 되었습니다. 모바일 버전이 있다는 걸 알게 되어 설치하고 플레이해봤는데, 마지막 문제가 정말 인상 깊었어요.

 

블럭을 움직여 빨간 사각형을 밖으로 빼내는 문제였습니다. 이건 누가 봐도 코딩으로 해결을 하라는 거죠. 바로 visual studio를 켜줍니다.

알고리즘

각 판의 상태를 정점으로, 한 상태에서 다른 상태로 이동할 수 있을 때 간선으로 연결을 해 주면 그래프가 만들어 집니다. 그럼 다익스트라나 BFS를 통해서 이 문제를 해결할 수 있습니다.

정점

빈 칸도 블럭이라 생각을 하고 각 블럭의 좌측 상단에 번호를 매깁니다. 빈칸은 0, 초록색은 1, 파란색은 2, 보라색은 3, 빨간색은 4로 번호를 매겨줍니다.

 

왼쪽 위부터 오른쪽으로 쭉 가면서 숫자를 읽어줍니다. 숫자가 없는 곳은 넘어갑니다. 위 그림은 221431010221이 됩니다. 이 숫자를 5진수로 생각하고 10진수로 변환하면 숫자 하나로 판의 상태를 나타낼 수 있습니다. 최악의 경우 229488275니까 long long으로 충분히 표현 가능합니다.

그럼 다음 알고리즘을 생각할 수 있습니다.

  1. 처음 판의 상태를 큐에 넣어둡니다..
  2. 큐에 있는 숫자를 꺼내고 해당 숫자를 판으로 바꿉니다.
  3. 이동 가능한 모든 판의 상태를 구하고 해당 판을 큐에 넣어둡니다.
  4. 빨간 블럭이 출구로 도달하면 프로그램을 종료합니다.

시간복잡도

bfs로 하면 $O(M)$에 해결이 가능하겠지만 저는 다익스트라를 이용해 $O(M\log M)$ 해결을 했습니다. $M$은 간선의 개수입니다.

정점의 개수는 $5^{12}$인것 같지만 사실 각 블럭의 개수는 변하지 않습니다. 즉 12개의 칸에 4 1개, 3 1개, 2 4개, 1 4개, 0 2개를 배치하는 경우의 수를 구하면 415800개가 됩니다.

블럭은 항상 빈칸으로 움직일 수 있습니다. 빈 칸이 2개고 각 빈 칸 마다 상하좌우로 들어올 수 있으니 각 상태 당 최대 8개의 이동이 가능합니다. 이러면 1초 안에는 충분히 해결할 수 있습니다.

코드

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<queue>
#include<map>
using namespace std;
priority_queue<pair<int, long long int>> q;
map<long long int, int>mm;
map<long long int, int>back;
int mapp[8][8], n = 4, m = 5,path[10000], cnt = 0;

// 숫자를 판으로
void get(long long int v) {
    int i, j, base = 48828125; // 5**11
    // 초기화
    for (i = 0;i <= n+1;i++) {
        for (j = 0;j <= m+1;j++) {
            mapp[i][j] = -1;
        }
    }
    for (i = 1;i <= n;i++) {
        for (j = 1;j <= m;j++) {
            if (mapp[i][j] != -1) continue;
            if (v / base == 0) mapp[i][j] = 0;
            else if (v / base == 1) mapp[i][j] = 1;
            else if (v / base == 2) {
                mapp[i][j] = 2;
                mapp[i][j + 1] = 2;
            }
            else if (v / base == 3) {
                mapp[i][j] = 3;
                mapp[i + 1][j] = 3;
            }
            else if (v / base == 4) {
                mapp[i][j] = 4;
                mapp[i + 1][j] = 4;
                mapp[i][j + 1] = 4;
                mapp[i + 1][j + 1] = 4;
            }v %= base;
            base /= 5;
        }
    }
}

// 판을 숫자로
long long int get_v() {
    int i, j;
    long long int v = 0, base = 48828125; // 5**11
    for (i = 1;i <= n;i++) {
        for (j = 1;j <= m;j++) {
            if (mapp[i][j] == 0) { base /= 5;continue; }
            else if (mapp[i][j] == 1) {
                v += base;
                base /= 5;
            }
            else if (mapp[i][j] == 2) {
                v += base*2;
                j++;
                base /= 5;
            }
            else if (mapp[i][j] == 3) {
                if (mapp[i - 1][j] == 3) continue;
                v += base * 3;
                base /= 5;
            }
            else if (mapp[i][j] == 4) {
                if (mapp[i + 1][j + 1] != 4) continue;
                v += base * 4;
                base /= 5;
            }
        }
    }
    return v;
}

// 움직일 수 있는 모든 경우 계산
void move(long long int vv,int cc) {
    int i, j, tmap[8][8];
    long long int v;
    for (i = 0;i <= n+1;i++) {
        for (j = 0;j <= m+1;j++) {
            tmap[i][j] = mapp[i][j];
        }
    }
    for (i = 1;i <= n;i++) {
        for (j = 1;j <= m;j++) {
            if (tmap[i][j] == -1) continue;
            if (tmap[i][j] == 1) {
                if (mapp[i][j + 1] == 0) {
                    mapp[i][j] = 0;
                    mapp[i][j + 1] = 1;
                    v = get_v();
                    if (mm.find(v) == mm.end()) {
                        mm[v] = cc + 1;
                        q.push({ -(cc + 1), v });
                        back[v] = vv;
                    }
                    mapp[i][j] = 1;
                    mapp[i][j + 1] = 0;
                }
                if (mapp[i][j - 1] == 0) {
                    mapp[i][j] = 0;
                    mapp[i][j - 1] = 1;
                    v = get_v();
                    if (mm.find(v) == mm.end()) {
                        mm[v] = cc + 1;
                        q.push({ -(cc + 1), v });
                        back[v] = vv;
                    }
                    mapp[i][j] = 1;
                    mapp[i][j - 1] = 0;
                }
                if (mapp[i + 1][j] == 0) {
                    mapp[i][j] = 0;
                    mapp[i + 1][j] = 1;
                    v = get_v();
                    if (mm.find(v) == mm.end()) {
                        mm[v] = cc + 1;
                        q.push({ -(cc + 1), v });
                        back[v] = vv;
                    }
                    mapp[i][j] = 1;
                    mapp[i + 1][j] = 0;
                }
                if (mapp[i - 1][j] == 0) {
                    mapp[i][j] = 0;
                    mapp[i - 1][j] = 1;
                    v = get_v();
                    if (mm.find(v) == mm.end()) {
                        mm[v] = cc + 1;
                        q.push({ -(cc + 1), v });
                        back[v] = vv;
                    }
                    mapp[i][j] = 1;
                    mapp[i - 1][j] = 0;
                }
                tmap[i][j] = -1;
            }
            if (tmap[i][j] == 2) {
                if (mapp[i][j + 2] == 0) {
                    mapp[i][j] = 0;
                    mapp[i][j + 2] = 2;
                    v = get_v();
                    if (mm.find(v) == mm.end()) {
                        mm[v] = cc + 1;
                        q.push({ -(cc + 1), v });
                        back[v] = vv;
                    }
                    mapp[i][j] = 2;
                    mapp[i][j + 2] = 0;
                }
                if (mapp[i][j - 1] == 0) {
                    mapp[i][j+1] = 0;
                    mapp[i][j - 1] = 2;
                    v = get_v();
                    if (mm.find(v) == mm.end()) {
                        mm[v] = cc + 1;
                        q.push({ -(cc + 1), v });
                        back[v] = vv;
                    }
                    mapp[i][j+1] = 2;
                    mapp[i][j - 1] = 0;
                }
                if (mapp[i + 1][j] == 0 && mapp[i + 1][j + 1] == 0) {
                    mapp[i][j] = 0;
                    mapp[i + 1][j] = 2;
                    mapp[i][j + 1] = 0;
                    mapp[i + 1][j + 1] = 2;
                    v = get_v();
                    if (mm.find(v) == mm.end()) {
                        mm[v] = cc + 1;
                        q.push({ -(cc + 1), v });
                        back[v] = vv;
                    }
                    mapp[i][j] = 2;
                    mapp[i + 1][j] = 0;
                    mapp[i][j + 1] = 2;
                    mapp[i + 1][j + 1] = 0;
                }
                if (mapp[i - 1][j] == 0 && mapp[i - 1][j + 1] == 0) {
                    mapp[i][j] = 0;
                    mapp[i - 1][j] = 2;
                    mapp[i][j + 1] = 0;
                    mapp[i - 1][j + 1] = 2;
                    v = get_v();
                    if (mm.find(v) == mm.end()) {
                        mm[v] = cc + 1;
                        q.push({ -(cc + 1), v });
                        back[v] = vv;
                    }
                    mapp[i][j] = 2;
                    mapp[i - 1][j] = 0;
                    mapp[i][j + 1] = 2;
                    mapp[i - 1][j + 1] = 0;
                }
                tmap[i][j] = -1;
                tmap[i][j+1] = -1;
            }
            if (tmap[i][j] == 3) {
                if (mapp[i][j + 1] == 0 && mapp[i+1][j + 1] == 0) {
                    mapp[i][j] = 0;
                    mapp[i][j + 1] = 3;
                    mapp[i+1][j] = 0;
                    mapp[i+1][j + 1] = 3;
                    v = get_v();
                    if (mm.find(v) == mm.end()) {
                        mm[v] = cc + 1;
                        q.push({ -(cc + 1), v });
                        back[v] = vv;
                    }
                    mapp[i][j] = 3;
                    mapp[i][j + 1] = 0;
                    mapp[i + 1][j] = 3;
                    mapp[i + 1][j + 1] = 0;
                }
                if (mapp[i][j - 1] == 0 && mapp[i + 1][j - 1] == 0) {
                    mapp[i][j] = 0;
                    mapp[i][j - 1] = 3;
                    mapp[i + 1][j] = 0;
                    mapp[i + 1][j - 1] = 3;
                    v = get_v();
                    if (mm.find(v) == mm.end()) {
                        mm[v] = cc + 1;
                        q.push({ -(cc + 1), v });
                        back[v] = vv;
                    }
                    mapp[i][j] = 3;
                    mapp[i][j - 1] = 0;
                    mapp[i + 1][j] = 3;
                    mapp[i + 1][j - 1] = 0;
                }
                if (mapp[i + 2][j] == 0) {
                    mapp[i][j] = 0;
                    mapp[i + 2][j] = 3;
                    v = get_v();
                    if (mm.find(v) == mm.end()) {
                        mm[v] = cc + 1;
                        q.push({ -(cc + 1), v });
                        back[v] = vv;
                    }
                    mapp[i][j] = 3;
                    mapp[i + 2][j] = 0;
                }
                if (mapp[i - 1][j] == 0) {
                    mapp[i+1][j] = 0;
                    mapp[i - 1][j] = 3;
                    v = get_v();
                    if (mm.find(v) == mm.end()) {
                        mm[v] = cc + 1;
                        q.push({ -(cc + 1), v });
                        back[v] = vv;
                    }
                    mapp[i+1][j] = 3;
                    mapp[i - 1][j] = 0;
                }
                tmap[i][j] = -1;
                tmap[i+1][j] = -1;
            }
            if (tmap[i][j] == 4) {
                if (mapp[i][j + 2] == 0 && mapp[i + 1][j + 2] == 0) {
                    mapp[i][j] = 0;
                    mapp[i][j + 2] = 4;
                    mapp[i + 1][j] = 0;
                    mapp[i + 1][j + 2] = 4;
                    v = get_v();
                    if (mm.find(v) == mm.end()) {
                        mm[v] = cc + 1;
                        q.push({ -(cc + 1), v });
                        back[v] = vv;
                    }
                    mapp[i][j] = 4;
                    mapp[i][j + 2] = 0;
                    mapp[i + 1][j] = 4;
                    mapp[i + 1][j + 2] = 0;
                }
                if (mapp[i][j - 1] == 0 && mapp[i + 1][j - 1] == 0) {
                    mapp[i][j+1] = 0;
                    mapp[i][j - 1] = 4;
                    mapp[i + 1][j+1] = 0;
                    mapp[i + 1][j - 1] = 4;
                    v = get_v();
                    if (mm.find(v) == mm.end()) {
                        mm[v] = cc + 1;
                        q.push({ -(cc + 1), v });
                        back[v] = vv;
                    }
                    mapp[i][j+1] = 4;
                    mapp[i][j - 1] = 0;
                    mapp[i + 1][j+1] = 4;
                    mapp[i + 1][j - 1] = 0;
                }
                if (mapp[i + 2][j] == 0 && mapp[i + 2][j+1] == 0) {
                    mapp[i][j] = 0;
                    mapp[i + 2][j] = 4;
                    mapp[i][j + 1] = 0;
                    mapp[i + 2][j + 1] = 4;
                    v = get_v();
                    if (mm.find(v) == mm.end()) {
                        mm[v] = cc + 1;
                        q.push({ -(cc + 1), v });
                        back[v] = vv;
                    }
                    mapp[i][j] = 4;
                    mapp[i + 2][j] = 0;
                    mapp[i][j + 1] = 4;
                    mapp[i + 2][j + 1] = 0;
                }
                if (mapp[i - 1][j] == 0 && mapp[i - 1][j+1] == 0) {
                    mapp[i + 1][j] = 0;
                    mapp[i - 1][j] = 4;
                    mapp[i + 1][j+1] = 0;
                    mapp[i - 1][j+1] = 4;
                    v = get_v();
                    if (mm.find(v) == mm.end()) {
                        mm[v] = cc + 1;
                        q.push({ -(cc + 1), v });
                        back[v] = vv;
                    }
                    mapp[i + 1][j] = 4;
                    mapp[i - 1][j] = 0;
                    mapp[i + 1][j + 1] = 4;
                    mapp[i - 1][j + 1] = 0;
                }
                tmap[i][j] = -1;
                tmap[i + 1][j] = -1;
                tmap[i][j + 1] = -1;
                tmap[i + 1][j + 1] = -1;
            }
        }
    }
}

int main() {
    int i, j, k;
    // 초기값
    q.push({ 0,120953811 });
    // 해당 판의 상태가 어디서 왔는지 저장
    back[120953811] = -1;
    while (q.size()) {
        int c = -q.top().first;
        long long int v = q.top().second;
        q.pop();
        if (mm[v] != c) continue;
        get(v);
        // 종료 시점
        if (mapp[2][5] == 4 && mapp[3][5] == 4) {
            printf("END!\n");
            // 경로 추출
            while (v != -1) {
                cnt++;
                path[cnt] = v;
                v = back[v];
            }
            
            // 판의 상태 출력
            for (i = cnt;i >= 1;i--) {
                get(path[i]);
                for (j = 1;j <= n;j++) {
                    for (k = 1;k <= m;k++) {
                        printf("%d", mapp[j][k]);
                    }
                    printf("\n");
                }
                printf("\n");
                scanf("%*c");
            }
            return 0;
        }
        move(v, c);
    }
    return 0;
}