플로이드 워셜(Floyd Warshall)
플로이드 워셜 알고리즘(Floyd-Warshall Algorithm)은 모든 지점에서 다른 모든 지점까지의 최단 경로를 모두 구해야하는 경우에 사용할 수 있는 경우이다. 다익스트라 알고리즘에서는 한 지점에서 다른 모든 지점까지의 최단거리인 반면, 플로이드 워셜은 모든 지점 to 모드 지점
인 것이다.
플로이드 워셜 알고리즘은 2차원 리스트
에 최단 거리 정보를 저장한다. 노드의 개수가 N 개일 때 알고리즘상으로 N 번의 단계를 수행하며, 단계마다 O(N^2)
의 연산을 통해 현재 노드를 거쳐가는 모든 경로를 고려한다. 따라서 총 시간 복잡도는 O(N^3)
이다.
- 플로이드 워셜 알고리즘을 사용해야 하는지에 대한 판단 기준
- 가중치 방향 그래프가 주어진다.
- A -> K -> B 이런식으로 어디를 거쳐간다.
- 시간 복잡도가 O(N^3) 이므로 N 의 범위 값이 500 이하여야 한다. (1초 기준)
플로이드 워셜 알고리즘 역시 다이나믹 프로그래밍에 속하므로 점화식이 중요한데 다음과 같다.
$$D(ab) = Min(D(ab), D(ak) + D(kb))$$
a 에서 b 로 가는 비용 = Min(기존에 저장된 a 에서 b 로 가는 비용 vs a 에서 k 를 거쳐 b 로 가는 비용) 이라고 나타낼 수 있다.
플로이드 워셜 알고리즘에서는 INF(무한의 값을 의미하는 상수)
를 초기화할 때 Integer.MAX_VALUE 로 초기화하면 안된다. D(ak) + D(kb) 이 부분을 수행할 때 MAX_VALUE 를 넘어가게되면 INTEGER.MIN_VALUE 에 가까운 값이 배열에 들어가게 되기 때문이다.
private static final int INF = (int) 1e9; // 무한을 의미하는 값으로 10억을 설정
구현
다익스트라 알고리즘과 마찬가지로 플로이드 워셜 알고리즘은 최단 경로(Shortest Path)
를 해결하기 위한 알고리즘으로 도로, 길찾기 문제에 주로 사용되며, 가중치 그래프가 주어진다. 만약, 문제에서 a 에서 k 를 거쳐 b 로 간다
라는 내용이 존재한다면 플로이드 워셜 알고리즘을 사용하여 해결해야하는 문제일 가능성이 높다.
// 플로이드 워셜 알고리즘 : 모든 지점에서 다른 모든 지점까지의 최단 경로를 모두 구해야 할 때 사용
public class Main {
private static final int INF = (int) 1e9; // 다익스트라 알고리즘과는 달리 여기서는 Integer.MAX_VALUE 로 초기화하면 안된다.
private static int n; // 노드의 개수
private static int m; // 간선의 개수
// 인접 행렬 방식(Adjacency Matrix)
private static int[][] graph = new int[501][501]; // 노드의 개수는 최대 500 이라 가정
public static void main(String[] args) {
input();
floydwarshall();
output();
}
private static void input() {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
m = sc.nextInt();
// 최단 거리 테이블을 모두 무한으로 초기화
for (int i = 0; i < 501; i++) {
Arrays.fill(graph[i], INF);
}
// 자기 자신에서 자기 자신으로 가는 비용은 0으로 초기화
for (int a = 1; a <= n; a++) {
for (int b = 1; b <= n; b++) {
if (a == b) {
graph[a][b] = 0;
}
}
}
// 각 간선에 대한 정보를 입력 받아, 그 값으로 초기화
for (int i = 0; i < m; i++) {
// A 에서 B 로 가는 비용은 C 라고 설정
int a = sc.nextInt();
int b = sc.nextInt();
int c = sc.nextInt();
graph[a][b] = c;
}
}
private static void floydwarshall() {
// 점화식에 따라 플로이드 워셜 알고리즘을 수행
for (int k = 1; k <= n; k++) {
for (int a = 1; a <= n; a++) {
for (int b = 1; b <= n; b++) {
graph[a][b] = Math.min(graph[a][b], graph[a][k] + graph[k][b]);
}
}
}
}
private static void output() {
// 수행된 결과를 출력
for (int a = 1; a <= n; a++) {
for (int b = 1; b <= n; b++) {
// 도달할 수 없는 경우, 무한(INFINITY)이라고 출력
if (graph[a][b] == INF) {
System.out.print("INFINITY");
}
// 도달할 수 있는 경우 거리를 출력
else {
System.out.print(graph[a][b] + " ");
}
}
System.out.println();
}
}
}
References
'Algorithms > 기본 지식' 카테고리의 다른 글
신장 트리와 크루스칼 알고리즘 (0) | 2021.12.13 |
---|---|
서로소 집합 (0) | 2021.12.12 |
최단 경로와 다익스트라 알고리즘 (0) | 2021.12.11 |
다이나믹 프로그래밍 (0) | 2021.12.11 |
결정 알고리즘과 파라메트릭 서치 (1) | 2021.12.09 |