플로이드 워셜 알고리즘
- 모든 지점에서 다른 모든 지점까지의 최단 경로를 모두 구해야 하는 경우에 사용
다익스트라 알고리즘은 단계마다 최단 거리를 가지는 노드를 하나씩 반복적으로 선택한다.
그리고 해당 노드를 거쳐 가는 경로를 확인하며, 최단 거리 테이블을 갱신하는 방식으로 동작한다.
플로이드 워셜 또한 단계마다 '거쳐 가는 노드'를 기준으로 알고리즘을 수행한다. 하지만 매번 방문하지 않은 노드 중에서 최단 거리를 갖는 노드를 찾을 필요가 없다는 점이 다르다.
노드의 개수가 N개일 때 알고리즘 상으로 N번의 단계를 수행하며,
단계마다 \(O(N^2)\)의 연산을 통해 '현재 노드를 거쳐 가는' 모든 경로를 고려한다.
따라서 플로이드 워셜 알고리즘의 총시간 복잡도는 \(O(N^3)\)이다.
다익스트라 알고리즘에서는 출발 노드가 1개이므로 다른 모든 노드까지의 최단 거리를 저장하기 위해 1차원 리스트를 이용했다. 반면에 플로이드 워셜 알고리즘은 다익스트라 알고리즘과는 다르게 2차원 리스트에 '최단 거리' 정보를 저장한다.
모든 노드에 대하여 다른 모든 노드로 가는 최단 거리 정보를 담아야 하기 때문이다. 다시 말해 2차원 리스트를 처리해야 하므로 N번의 단계에서 매번 \(O(N^2)\)의 시간이 소요된다.
예를 들어 1번 노드에 대해서 확인할 때는 1번 노드를 중간에 거쳐 지나가는 모든 경우를 고려하면 된다.
정확히는 A → 1번 노드 → B로 가는 비용을 확인한 후에 최단 거리를 갱신한다.
따라서 알고리즘에서는 현재 확인하고 있는 노드를 제외하고 N-1개의 노드 중에서 서로 다른 노드 (A, B)쌍을 선택한다.
이후에 A → 1번 노드 B로 가는 비용을 확인한 뒤에 최단거리를 갱신한다.
즉, \(_{N-1}P_{2}\) 개의 쌍을 단계마다 반복해서 확인하면 된다.
점화식은 다음과 같다.
\(D\! _{ab} = min(D\! _{ab}, D\! _{ak} + D\! _{kb})\)
예시로 보자.
0. 초기상태
그래프를 입력받고
각 그래프마다 다이렉트로 연결된 노드 간의 거리로 초기화를 해준다.
1. 1번 노드 거치는 경우
2 > 1 > 4 를 거치는 경우가 기존의 '무한' 값보다 작기 때문에 3+6 = 9로 값을 갱신해준다.
마찬가지로 3 > 1 > 2 로 가는 경우도 5 + 4 = 9로 무한보다 작기에 갱신해준다.
2. 2번 노드를 거치는 경우
1 > 2 >3 을 거치는 경우가 '무한' 에서 4 + 7 = 11로 갱신
3. 3번 노드를 거치는 경우
4 > 3 > 1 로 가는 거리가 '무한' 에서 2 + 5 = 7 로 갱신
4 > 3 > 2 로 가는 거리가 '무한'에서 2 + 9 (3 > 2로 가는 값이 9 (3 > 1 > 2 = 9)) = 11로 갱신
4. 4번 노드를 거치는 경우
1 > 4 > 3 이 6 + 2 = 8 로 기존의 '11'보다 작으므로 8로 갱신
# 9-3 최단 경로 플로이드 워셜
INF = int(1e9) #무한 값
# 노드의 개수 및 간선의 개수 입력받기
n = int(input())
m = int(input())
# 2차원 리스트 (그래프 표현) 만들고, 모든 값을 무한으로 초기화
graph = [ [INF] * (n+1) for _ in range(n+1) ]
# 자기 자신에서 자기 자신으로 가는 비용은 0 으로 초기화
for a in range(1, n+1):
for b in range(1, n+1):
if a == b:
graph[a][b] = 0
# 각 간선에 대한 정보 입력받아, 그 값으로 초기화 (step 0의 단계)
for _ in range(m):
# A에서 B로 가는 비용은 C라고 설정
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] == INF:
print("INF")
else:
print(graph[a][b], end=" ")
print()
(자바 코드)
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.StringTokenizer;
public class Main {
public static void main(String[] args) throws NumberFormatException, IOException {
// TODO Auto-generated method stub
int INF = (int)1e9;
int n, m;
int [][] graph = new int[501][501]; //501개가 최대로 가정
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
n = Integer.parseInt(br.readLine());
m = Integer.parseInt(br.readLine());
for(int i=0; i<501; i++)
{
Arrays.fill(graph[i], INF);
}
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++)
{
StringTokenizer st = new StringTokenizer(br.readLine());
int a = Integer.parseInt(st.nextToken());
int b = Integer.parseInt(st.nextToken());
int c = Integer.parseInt(st.nextToken());
graph[a][b] = c;
}
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]);
}
}
}
for(int a=1; a<=n; a++)
{
for(int b=1; b<=n; b++)
{
if(graph[a][b] == INF)
System.out.print("INF");
else
System.out.print(graph[a][b] + " ");
}
System.out.println();
}
}
}
'Algorithm > 이것이 코딩테스트다' 카테고리의 다른 글
[Python][이코테] 최단 경로 / 미래 도시 (0) | 2023.02.03 |
---|---|
[Python][이코테] 최단경로 / 다익스트라 알고리즘(2) (0) | 2022.11.15 |
[Python][이코테] 최단경로 / 다익스트라 알고리즘(1) (0) | 2022.11.09 |
[Python][이코테] 효율적인 화폐 구성 / DP (0) | 2022.11.01 |
[Python][이코테] 바닥 공사 / DP (0) | 2022.10.26 |
댓글