벨만-포드 알고리즘이란? (Bellman-Ford's algorihm)
1. 단일 시작점 알고리즘
시작점에서 다른 모든 정점까지의 최단 거리를 찾는다.
2. 방향 그래프
방향 그래프를 가정으로 둔다.
따라서, 무방향 그래프가 주어진다면 간선을 쪼개 방향 그래프로 바꿔야 한다.
3 음수 가중치
음수가 아닌 가중치만 다루는 다익스트라와 다르게 음수 가중치에서도 최단 거리 문제를 정의할 수 있는 알고리즘이다. 또한, 음수 싸이클로 인해 최단 거리를 정의할 수 없는 경우도 알아낼 수 있다.
동작 과정
1 상한을 두고 최단 거리 줄여나가기
upper[i] : 정점 i까지의 최단 거리의 상한을 의미한다. 알고리즘이 진행되며 upper 오차를 줄여나간다. 최종적으로 작동이 완료되면 upper값들이 최단 거리가 된다.
어떻게 오차를 줄여나갈까
2 최단 거리 특징을 이용하여 upper 줄이기 ( relax )
두 정점 u, v가 있고, u -> v로 w가중치로 연결되어 있다. 이 때, 최단 거리[u] + w >= 최단 거리 [v]는 항상 성립한다. 왜냐하면, u를 거쳐 v로 가는 경우가 최단 거리라면 등호가 성립하고, u 말고 다른 루트가 있다면 u를 거치는 것이 더 크기 때문이다.
upper에서 다음과 같은 상황을 생각해볼 수 있다. upper[u] + w < upper[v]
이때, upper[v] = upper[u] + w로 줄일 수 있다. 왜냐하면, upper[u]는 항상 최단거리 u보다 같거나 크기 때문이다. ( upper[v]를 최단거리로 줄여야 한다. ) 이 과정을 relax라고 부른다.
relax를 여러 번 모든 간선에서 진행하게 되면 upper[u]가 최단거리가 되며, 이어서 upper[v] 또한 최단거리가 된다. 그
그럼 몇 번이나 모든 간선에서 위 과정을 진행해야할까
3 최대 V-1번 relax로 최단 거리 찾기
1 번 모든 간선에서 relax 진행.
upper에서 start만 0으로, 나머지는 큰 값으로 초기화
start에서 가장 가까운 최단 거리 a가 있다면, upper[start] + weight < upper[a] 가 성립된다.
여기서, upper[start]는 0이고 weight는 입력으로 주어지기 때문에, dist[a] = upper[a] = weight로 최단 거리를 찾을 수 있다.
2 번 모든 간선에서 진행.
a까지의 최단 거리를 찾았으므로 한 번 더 모든 간선에서 오차를 줄이면, a 바로 다음 최단 거리의 b를 찾을 수 있다. 왜냐하면 a의 최단 거리를 이미 찾았기 때문에 b도 바로 찾을 수 있다.
...
이와 같이, x 번 모든 간선에서 relax를 진행하면, x개 이하 간선을 거쳐야 하는 정점의 최단 거리를 알 수 있다.
최악의 경우로, 가장 먼 간선은 V-1번 떨어져 있으므로 최대 V-1번 동작하면 모든 최단 거리를 알아낼 수 있다.
+ 진행 중 relax가 하나라도 안된다면 더 이상 최단 거리를 찾을 수 없으므로 종료한다.
음수 싸이클 판정
음수 싸이클이 있다면, V번 째에도 relax가 적어도 한 번은 발생한다.
왜냐하면, 계속해서 최단 거리를 찾아내기 때문이다.
ex) 두 정점 u, v사이에 음수 싸이클이 존재한다고 가정하자. 그럼 u에서 v로, v에서 u로 최단 거리가 계속 줄어들게 된다.
시간 복잡도
최대 V번 모든 간선 E를 확인하기 때문에 O(VE)
주의해야할 점
특정 정점까지 경로가 존재하는지 알기 위해 upper값이 초기에 설정한 max인지 아닌지로 구분하면 안 된다.
왜냐하면, 음수 싸이클이 존재하면 최단 거리가 정의되지 않아 max 또는 다른 값으로 지정되기 때문이다.
( 계속 최단거리를 찾아 값이 작게 변한다. )
따라서, 마지막 V번째 순회에서 음수 가중치가 없을 때만 최단 거리를 정의할 수 있다.
구현
upper를 Integer.MAX_VALUE 초기화 시 비교 주의
Integer.MAX_VALUE + cost >, < Integer.MAX_VALUE가 무조건 TRUE 이기 때문.
따라서, upper[u] + cost > upper[v] 를 upper[u] != Integer.MAX_VALUE 일때만 비교해야 한다.
또한, 시작 정점에서 가장 가까운 정점부터 최솟값을 찾아가기에 문제가 없다.
최단 거리 비교시, upper[u]가 초기 설정한 max값이라면 비교하지 말기
이론적으로는 upper[u]가 무한이라면 +- 연산을 해도 무한이기 때문에 upper[v]는 그대로 무한이다.
하지만, 코드에서는 '무한'이 아니고 '큰 수'이기 때문에 의도하지 않게 값이 변할 수 있다.
예를 들어, 시작점에서 다른 정점까지 이동가능한 것을 찾을 때, '큰 수'를 이용하여 비교한다면 잘못된 답이 된다.
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
|
// 최단 거리를 찾아 upper에 저장 후 true 리턴
// 하지만, 음수 싸이클이 존재하면 false 리턴
static boolean bellman_ford() {
boolean updated = false;
upper[1] = 0;
// N 번 검사. N-1번 동안 최단 거리 생성 후 N 번째에서 음수 싸이클 검사
for(int cnt = 0 ; cnt < N ; cnt++) {
updated = false;
// 모든 간선 검사
for(int here = 1 ; here <= N ; here++) {
for (node adj : adjList[here]) {
if( upper[here] != Integer.MAX_VALUE && upper[adj.num] > upper[here] + adj.weight) {
upper[adj.num] = upper[here] + adj.weight;
updated = true;
}
}
}
if(!updated)
break;
}
// N 번째에서도 update 됐다면 음수 싸이클이 존재해서 return false
if(updated)
return false;
return true;
}
|
cs |
'CS > Algorithm' 카테고리의 다른 글
구현) 정렬 (0) | 2020.03.21 |
---|---|
구현) 큐, 스택 (0) | 2020.03.17 |
선형 자료 구조 (0) | 2020.03.17 |
플로이드 와샬 알고리즘 (0) | 2020.03.16 |
다익스트라(Dijkstra) 알고리즘 ( 자바 ) (0) | 2020.03.01 |