알고리즘/BOJ 문제풀이

[Bellman-Ford]BOJ 3860_할로윈 묘지

4Legs 2020. 11. 3. 16:25

문제 링크 : www.acmicpc.net/problem/3860

 

3860번: 할로윈 묘지

오늘은 할로윈이다. 상근이와 친구들은 할로윈을 기념하기 위해 묘지를 방문했다. 상근이와 친구들은 한 명씩 묘지로 들어가고, 혼자서 묘지의 출구를 찾아야 한다. 이제, 상근이의 차례가 돌아

www.acmicpc.net

 

2차원 배열 형태의 그래프에 대한 벨만-포드 알고리즘 적용 문제이다.

문제에서 제시된 예제는 다음과 같다.

2차원 배열 형태의 예제

이러한 2차원 배열 형태의 예제를 우리에게 익숙한 일반 그래프 형태로 나타내면 아래와 같다.

그림에서 실선으로 표현된 간선들의 가중치는 모두 1이다.

따라서, 주어진 2차원 배열 Board를 그래프의 형태로 바꾸고,

변환한 그래프에 대해 벨만-포드 알고리즘을 적용하면 된다.

다만 BOJ1219_오민식의 고민 문제와는 달리,

이 문제는 음의 사이클이 발생하는 노드에 도달 가능한지 여부는 상관하지 않는다.

문제의 서술이 애매하긴 하지만 음의 사이클이 발생한다면 무조건 Never를 우선 출력한다.

 

이 풀이에서는 2차원 배열을 변환한 그래프를 1차원 배열 형태로 저장해 사용하였다. (최대 900)

[코드]

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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <vector>
#include <memory.h>
 
#define INF 99999999
 
using namespace std;
typedef pair<intint> p;
typedef pair<p, int> hole;
 
pair<intint> dir[4= { {01}, {10}, {0-1}, {-10} };
int w, h, g, e, board[900];
int dist[900];
vector<hole> holes;
bool cycle = false;
 
void bellman(int n) {
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            if (dist[j] == INF) continue;
            //도착 지점에서 나가는 간선을 만들지 않는다.
            if (j == n - 1continue;
 
            int row = j / w;
            int col = j % w;
 
            //현재 좌표가 묘비라면, 이동하지 않음
            if (board[j] == -1continue;
 
            //현재 좌표가 귀신구멍이라면, 귀신구멍을 통한 이동만 고려
            if (board[j] > 0) {
                hole temp = holes[board[j] - 1];
                int newN = temp.first.second;
                int distval = temp.second;
                if (dist[newN] > dist[j] + distval) {
                    dist[newN] = dist[j] + distval;
                    //무한루프 발생시
                    if (i == n - 1) cycle = true;
                }
                continue;
            }
 
            //일반 잔디 칸일 경우, 4방으로 이동
            for (int k = 0; k < 4; k++) {
                int newR = row + dir[k].first;
                int newC = col + dir[k].second;
                if (newR < 0 || newR >= h || newC < 0 || newC >= w) continue;
                int newN = newR * w + newC;
                int distval = 1;
 
                //묘비는 이동 불가
                if (board[newN] == -1continue;
 
                //이어주기
                if (dist[newN] > dist[j] + distval) {
                    dist[newN] = dist[j] + distval;
                    //무한루프 발생시
                    if (i == n - 1) {
                        cycle = true;
                    }
                }
            }
        }
    }
}
 
void init() {
    int r, c, r2, c2, t;
    memset(board, 0sizeof(board));
    holes.clear();
 
    for (int i = 0; i < w * h; i++) dist[i] = INF;
 
    cin >> g;
    for (int i = 0; i < g; i++) {
        cin >> c >> r;
        board[r * w + c] = -1;    //묘비
    }
 
    cin >> e;
    int idx = 1;
    for (int i = 0; i < e; i++) {
        cin >> c >> r >> c2 >> r2 >> t;
        board[r * w  + c] = idx++;
        holes.push_back({ {r * w + c, r2 * w + c2}, t });    //귀신구멍
    }
 
    dist[0= 0;
    cycle = false;
}
 
int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(NULL);
 
    while (1) {
        cin >> w >> h;
        if (w == 0 && h == 0break;
        init();
        int finish = w * h - 1;
        bellman(finish + 1);
 
        if(cycle) printf("Never\n");
        else {
            if (dist[finish] == INF) printf("Impossible\n");
            else printf("%d\n", dist[finish]);
        }
    }
 
    return 0;
}
cs