⭐️ 플로이드 워셜 알고리즘(Floyd-Warshall Algorithm)
- 모든 지점에서 다른 모든 지점까지 최단 경로를 모두 구해야하는 경우 사용
- 다익스트라 알고리즘과 마찬가지로 단계마다 '거쳐가는 노드'를 기준으로 알고리즘 수행
- 매번 방문하지 않은 노드 중에서 최단 거리를 갖는 노드를 찾을 필요가 없다는 점이 다익스트라 알고리즘과 다름
- 노드의 개수가 N개일 때 알고리즘상으로 N번의 단계를 수행하며, 단계마다 O(N2)의 연산을 통해 현재 노드를 거쳐가는 모든 경로를 고려
- 따라서 플로이드 워셜 알고리즘의 총 시간복잡도는 O(N3)
- 다익스트라 알고리즘에서는 출발 노드가 1개이므로 다른 모든 노드까지의 최단 거리를 저장하기 위해 1차원 리스트 이용
- 반면 플로이드 워셜 알고리즘은 2차원 리스트에 최단거리 정보 저장
- 모든 노드에 대하여 다른 모든 노드로 가는 최단 거리 정보를 담아야 하기 때문
- 2차원 리스트를 처리해야 하므로 N번의 단계에서 매번 O(N2)의 시간이 소요
- 다익스트라 알고리즘은 그리디 알고리즘이지만 플로이드 워셜 알고리즘은 다이나믹 프로그래밍
- 노드의 개수가 N이라고 할 때, N번 만큼의 단계를 반복하며 점화식에 맞게 2차원 리스트를 갱신하기 때문
점화식
- 알고리즘 각 단계에서는 현재 확인하고 있는 노드를 제외하고, N-1개의 노드 중에서 서로 다른 노드 (A,B)쌍을 선택한다.
- 이후에 A → 1번 노드 → B로 가는 비용을 확인한 뒤에 최단 거리를 갱신한다.
- N−1P2 개의 쌍을 단계마다 반복해서 확인
- O(N−1P2)는 O(N2)이라고 생각할 수 있으므로 전체 시간 복잡도는 O(N3)
- K번의 단계에 대한 점화식은 다음과 같다.
Dab=min(Dab,Dak+Dkb)
- A에서 B로 가는 최소비용과 A에서 K를 거쳐 B로 가는 비용을 비교하여 더 작은 값으로 갱신한다.
- 즉, 바로 이동하는 거리가 특정한 노드를 거쳐서 이동하는 거리보다 더 많은 비용을 가진다면 이를 더 짧은 것으로 갱신
- 전체적으로 3중 반복문을 이용하여 점화식에 따라 최단 거리 테이블을 갱신하면 문제를 해결할 수 있다.
그림으로 이해하기
STEP 0
- 초기 테이블 설정하기
- 2차원 리스트에서 각 값에 해당하는 Dab는 a에서 b로 가는 최단 거리이다.
- 그래프에서 연결된 간선은 단순히 그 값을 채워 넣고 연결되지 않은 간선은 '무한' 값을 넣는다.
- 자기 자신으로 가는 비용은 0이므로, 1≤i≤n의 범위를 가지는 모든 i에 대하여 Dii는 0으로 초기화
STEP 1
- 1번 노드를 거쳐 가는 경우 계산
- 2번 노드를 제외한 2, 3, 4번 노드에서 2개의 노드를 뽑는 경우를 고려
- (2, 3), (2, 4), (3, 2), (3, 4), (4, 2), (4, 3)
- 아래 6가지 경우를 확인하며 값을 계산하여 갱신
D23=min(D23,D21+D13)
D24=min(D24,D21+D14)
D32=min(D32,D31+D12)
D34=min(D34,D31+D14)
D42=min(D42,D41+D12)
D43=min(D43,D41+D13)
STEP 2
- 2번 노드를 거쳐 가는 경우 계산
- 2번 노드를 제외한 1, 3, 4번 노드에서 2개의 노드를 뽑는 경우를 고려
- (1, 3), (1, 4), (3, 1), (3, 4), (4, 1), (4, 3)
- 아래 6가지 경우를 확인하며 값을 계산하여 갱신
D13=min(D13,D12+D23)
D14=min(D14,D12+D24)
D31=min(D31,D32+D21)
D34=min(D34,D32+D24)
D41=min(D41,D42+D21)
D43=min(D43,D42+D23)
STEP 3
- 3번 노드를 거쳐 가는 경우 계산
- 3번 노드를 제외한 1, 2, 4번 노드에서 2개의 노드를 뽑는 경우를 고려
- (1, 2), (1, 4), (2, 1), (2, 4), (4, 1), (4, 2)
- 아래 6가지 경우를 확인하며 값을 계산하여 갱신
D12=min(D12,D13+D32)
D14=min(D14,D13+D34)
D21=min(D21,D23+D31)
D24=min(D24,D23+D34)
D41=min(D41,D43+D31)
D42=min(D42,D43+D32)
STEP 4
- 4번 노드를 거쳐가는 경우 계산
- 4번 노드를 제외한 1, 2, 3번 노드에서 2개의 노드를 뽑는 경우를 고려
- (1, 2), (1, 3), (2, 1), (2, 3), (3, 1), (3, 2)
- 아래 6가지 경우를 확인하며 값을 계산하여 갱신
D12=min(D12,D14+D42)
D13=min(D13,D14+D43)
D21=min(D21,D24+D41)
D23=min(D23,D24+D43)
D31=min(D31,D34+D41)
D32=min(D32,D34+D42)
최종 결과
- 모든 노드에서 모든 노드로 가는 최단 거리 정보를 담은 테이블
✏️ 구현하기
- 초기에는 각 노드 쌍 사이의 거리를 무한으로 설정하고 자기 자신으로 가는 거리는 0으로 설정한다.
- 입력으로 주어진 간선 정보를 바탕으로 그래프를 초기화한다.
- 플로이드 워셜 알고리즘을 사용하여 최단 경로를 구한다.
- 중간 노드를 거쳐서 가는 경로가 더 짧은 경우, 해당 경로로 거리를 갱신한다.
- 최단 경로를 출력한다.
- 도달할 수 없는 경우 "INFINITY"를 출력하고, 도달할 수 있는 경우에는 최단 거리를 출력한다.
- Python으로 작성한 플로이드워셜 알고리즘은 다음과 같다. (코드 출처)
INF = int(1e9)
n = int(input())
m = int(input())
graph = [[INF] * (n + 1) for _ in range(n + 1)]
for a in range(1, n + 1):
for b in range(1, n + 1):
if a == b:
graph[a][b] = 0
for _ in range(m):
a, b, c = map(int, input().split())
graph[a][b] = c
for k in range(1, n + 1):
for a in range(1, n + 1):
for b in range(1, n + 1):
graph[a][b] = min(graph[a][b], graph[a][k] + graph[k][b])
for a in range(1, n + 1):
for b in range(1, n + 1):
if graph[a][b] == 1e9:
print("INFINITY", end=" ")
else:
print(graph[a][b], end=" ")
print()
- 위에서 살펴본 예제를 입력한 결과는 다음과 같다.
4
7
1 2 4
1 4 6
2 1 3
2 3 7
3 1 5
3 4 4
4 3 2
0 4 8 6
3 0 7 9
5 9 0 4
7 11 2 0
📍 References