최단 경로 찾기 알고리즘(3) - 플로이드 와샬(Floyd-Warshall)
플로이드 와샬 알고리즘
플로이드 와샬 알고리즘은 다익스트라 알고리즘, 벨만 포드와 같이 정점간의 최단거리를 찾기 위해 고안된 알고리즘 입니다.
그러나 다른 두 알고리즘과는 달리 플로이드 와샬 알고리즘은 한 정점으로부터가 아닌 모든 정점으로부터 모든 정점까지의 최단거리를 구하는 알고리즘입니다.
이것만 보아도 꽤나 큰 시간 복잡도를 가질 것 같다는 느낌이 들죠?^^*
개념
플로이드 와샬 알고리즘의 원리는 굉장히 간단합니다.
현재 계산된 한 정점 S로부터 다른 한 정점 E로 가는 것의 비용보다
또 다른 어떠한 정점 M을 거쳐 가는 것의 비용이 적은 경우
S -> E을 S -> M -> E로 변경하기만 하면됩니다.
거쳐가는 노드 M, 시작하는 노드 S, 도착하는 노드 E, 모두 N개의 노드를 한번씩 지정해야 하므로
시간복잡도는 O(|V|^3)이 됩니다.
|V| : 정점의 갯수
구현 코드
플로이드 와샬 알고리즘은 백준 11404 플로이드 문제를 통해 구현해보았습니다.
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
// 플로이드
package BOJ;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
public class BOJ11404 {
static int MAX = 1000000001;
static String 플로이드_와샬(int[][] 비용, int 도시수) {
for (int 거치는도시 = 0; 거치는도시 < 도시수; 거치는도시++) {
for (int 시작도시 = 0; 시작도시 < 도시수; 시작도시++) {
for (int 도착도시 = 0; 도착도시 < 도시수; 도착도시++) {
if (비용[시작도시][거치는도시] + 비용[거치는도시][도착도시] < 비용[시작도시][도착도시]) {
비용[시작도시][도착도시] = 비용[시작도시][거치는도시] + 비용[거치는도시][도착도시];
}
}
}
}
// 플로이드 와샬 알고리즘은 위에서 끝!
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 비용.length; i++) {
for (int j = 0; j < 비용[i].length; j++) {
sb.append(((비용[i][j] == MAX) ? 0 : 비용[i][j]) + " ");
}
sb.deleteCharAt(sb.length() - 1);
sb.append("\n");
}
return sb.toString();
}
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
int n = Integer.parseInt(br.readLine());
int[][] map = new int[n][n];
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
map[i][j] = (i == j) ? 0 : MAX;
}
}
int m = Integer.parseInt(br.readLine());
for (int i = 0; i < m; i++) {
String[] abc = br.readLine().split(" ");
int a = Integer.parseInt(abc[0]) - 1;
int b = Integer.parseInt(abc[1]) - 1;
int c = Integer.parseInt(abc[2]);
if (c < map[a][b]) map[a][b] = c;
}
bw.write(플로이드_와샬(map, n));
bw.flush();
}
}
주의해야 했던 점은 배열을 모두 최대값으로 지정해야하는데 이를 Integer.MAX_VALUE와 같은 타입 최댓값을 넣으면 안된다는 점입니다.
Integer.MAX_VALUE + 1은 Integer의 최소값이 되어버립니다.
시작 도시
에서 거치는 도시
로 가는 경로가 없어 비용[시작도시][거치는도시] = Integer.MAX_VALUE
이고
거치는 도시
에서 도착 도시
로 가는 경로의 비용은 1인 경우에
비용[시작도시][거치는도시] + 비용[거치는도시][도착도시]
는 Integer의 최소값이 되어버립니다.
결국 존재하는 경로가 없으므로 false가 되어야하는
비용[시작도시][거치는도시] + 비용[거치는도시][도착도시] < 비용[시작도시][도착도시]
가
true 가 되어버릴 수 있습니다.
제가 했던 바보같은 실수
문제를 보면 시작 도시와 도착 도시를 연결하는 노선은 하나가 아닐 수 있다.
라는 문장이 있습니다.
무조건 경로의 최소값을 보여주면 되겠다 싶어 비용 표를 PriorityQueue<Integer>[n][n]
로 생성하려 했습니다.
구현하면서도 메모리 사용이 장난이 아니겠는데? 생각했습니다.
그냥 저장할때 작은 경우에만 저장하면 됐습니다^^;
댓글남기기