미로 찾기 문제 분석

 

미로 찾기 문제는 내가 직접 미로에 빠졌을 때 어떻게 탈출할 수 있을 지에 대한 관점으로 분석합니다. 먼저, 내가 서 있는 지점이 탈출구라면 탈출에 성공한 것이고 그 외에는 내가 지나온 길을 표시해서 다시 원점으로 돌아왔다면 그 길은 길이 아닌 것입니다. 만약 왔었던 길을 표시해주지 않으면 원점에서 동,서,남,북 방향으로 이동했을 때 그 지점들부터 다시 동,서,남,북으로 이동하면 왔던길을 다시 지나게 되어 무한루프가 발생하게 됩니다. 그래서 처음부터 내가 지날 수 있는 길들을 0으로 표시하고 벽들은 1로 표시합니다. 그리고 내가 지나간 지점들은 다 2로 표시해주고 그 길이 갈 수 없는 길이라면 3을 표시해줘서 다음에는 지나가지 않게 합니다.

 

미로 찾기 원리

 

 

먼저 이런 모양의 미로를 만듭니다. 회색은 벽이고 흰색은 길입니다.

먼저 동쪽으로 이동하는 경우만 생각합니다.

첫 번째 갈라지는 경우에서 남쪽으로 갈 때에 벽에 막히기 때문에 Blocked처리를 합니다.(더 이상 갈 수 없는 상태)

계속 나아가다 보면 또 벽에 부딪히게 됩니다.

그 부분 역시 Blocked처리를 합니다.

계속 이동해서 또 벽에 부딪히게 됩니다.

이 역시 벽에 부딪히고 처음에서 동쪽으로 출발한 쪽은 탈출구에 도달하는 길이 아니기 때문에 전체를 다 Blocked처리를 합니다. 그런 다음 처음부분에서 남쪽으로 이동하게 됩니다.

다른 막히는 길은 생략하고 탈출구로 향하는 길로 이동하는 경우만 봤을 때, 탈출구에 도달한다면 이동한 경로를 전부 다 Path(길)처리를 합니다. 만약 탈출구가 없다면 Path처리되는 곳은 없습니다.

 

 

 

미로 찾기 코드

 

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
#include <stdio.h>
 
const int N = 8;
const int PATHWAY_COLOUR = 0;//아직 한 번도 가보지 못한 cell
const int WALL_COLOUR = 1;//벽이라고 정해진 cell
const int BLOCKED_COLOUR = 3;//visited이며 출구까지의 경로가 있지 않음이 밝혀진 cell
const int PATH_COLOUR = 2;//visited이며 아직 출구로 가는 경로가 될 가능성이 있는 cell
int map[N][N] = {
    {00000001},
    {01101101},
    {00010001},
    {01001100},
    {01110011},
    {01000101},
    {00010001},
    {01110100}
};
 
int findMap(int x, int y) {
    if (x < 0 || y < 0 || x >= N || y >= N)
        return 0;
    else if (map[x][y] != PATHWAY_COLOUR)
        return 0;
    else if (x == N - 1 && y == N - 1) {
        map[x][y] = PATH_COLOUR;
        return 1;
    }
    else {
        map[x][y] = PATH_COLOUR;
        if (findMap(x - 1, y) || findMap(x, y + 1|| findMap(x + 1, y) || findMap(x, y - 1)) {
            return 1;
        }
        map[x][y] = BLOCKED_COLOUR;
        return 0;
    }
}
int main() {
    int i, j;
    printf("탈출 구 찾기 여부 : %d\n", findMap(00));
    for (i = 0; i < N; i++) {
        for (j = 0; j < N; j++) {
            printf("%d ",map[i][j]);
        }
        printf("\n");
    }
    return 0;
}
cs

map은 임의로 만들고 내가 갈 수 있는 길은 0, 벽은 1, 내가 지난 길은 2, 가봤었는데 탈출구로 갈 수 없는 길이면 3으로 배열에 표시해줄수 있는 Recursion을 만듭니다. 시작지점이 0,0이고 탈출구가 8,8인 9*9 크기의 맵을 기준으로 Recursion할 때 먼저 Base case를 지정해줍니다. 지금까지는 Base case가 1개인 간단한 예제들을 살펴봤지만 이 문제는 Base case가 여러개네요. 첫 번째 Base case는 점(x,y)가 0보다 작아지거나 N-1보다 커지는 경우들입니다. 더 이상 비교해 줄 공간이 없는 막힌 공간이기 때문에 Base case로 설정하는 것입니다. 두 번째 Base case는 내가 갈 수 있는 길(0)들이 아니면 return 0;으로 처리해줍니다. 이렇게 설정하면 내가 가야 될 길들만 갈 수 있기때문에 가는 길이 중복되지 않아서 무한반복이 발생하지 않습니다. 그리고 세 번째 Base case는 내가 밟고 있는 지점이 탈출구(N-1,N-1)라면 탈출성공이기 때문에 return 1;로 처리합니다. 이렇게 3개를 Base case 세 개를 설정한다면 모든 준비는 완료됩니다. 이제 탈출하기 위한 방향성만 설정해주면 문제가 풀립니다. 방향성을 설정하는 것은 동,서,남,북 방향으로 각각 findMap(x-1, y) || findMap(x, y+1) || findMap(x+1,y) || findMap(x,y-1) 방향으로 Recursion해주면 탈출구를 찾게 됩니다. 탈출구가 없다면 모든 길들이 다 BLOCKED_COLOUR처리가 되어서 최종적으로 return 0;을 만나게 됩니다. findMap(0,0)을 호출하고 그 후에 배열안에 결과값을 확인해본다면 길인 지점들이 모두 2로 표시되면서 출발점부터 탈출구까지를 잇게 됩니다.

 

 

 

2를 쭉 연결한다면 탈출구에 도착했다는 것을 알 수 있습니다. 만약 (7,6)지점을 벽으로 막게 된다면 전부 다 비교하게 되고 결국 탈출구를 찾지 못해 최종적으로 return 0;을 만나게 됩니다.

이런 종류에 문제는 Recursion으로 푸는 것이 간결하고 가독성이 좋은 것 같습니다. 앞으로 이걸 응용해서 최단거리, 어려운 미로찾기 문제를 도전해보겠습니다!

 

- 영리한 프로그래밍을 위한 알고리즘 (권오흠 교수님) 참고 -

+ Recent posts