모든 쌍 최단경로
모든 노드들 사이의 최단경로를 구하는 알고리즘
인접행렬의 다익스트라 알고리즘은 플로이드 워셜과 동일한 이지만,
희소그래프이면 인접리스트로 구현한 다익스트라를 N번 수행하는 경우는 보다 작기 때문에
만약 으로 시간초과가 난다면 다익스트라를 N번 돌려서 모든 정점간의 거리를 구하자.
다익스트라는 그리디 알고리즘이기 때문에 음의 가중치는 처리하지 못했다.
하지만 플로이드 워셜은 모든 경우를 다 고려하기 때문에 음의 가중치도 처리 가능하다
부분문제 정의
경유지 활용
플로이드-워셜 알고리즘은 임의의 노드 s에서 e까지 가는 데 걸리는 최단거리를 구하기 위해,
s와 e 사이의 노드인 m에 대해 (s에서 m까지 가는 데 걸리는 최단거리)와
(m에서 e까지 가는 데 걸리는 최단거리)를 이용한다.
임의의 노드 s 부터 e 까지 가는데 걸리는 최단거리를 d[s][e]라고 하자.
(처음에 d[s][e]의 값은 노드 s와 노드 e가 직접적으로 연결되어 있다면 그 노드의 가중치만큼, 그렇지 않다면 무한(INF)로 초기화한다.)
이 d[s][e]를 구하기 위해서, s와 e 사이의 모든 노드 m에 대해, 현재 d[s][e]에 저장되어 있는 값과, d[s][m]+d[m][e]의 값을 비교한다.
이 때 d[s][m]+d[m][e]의 값이 현재의 d[s][e] 값보다 작으면, d[s][e]를 d[s][m]+d[m][e] 의 값으로 업데이트한다.
출처-나무위키
나무위키가 설명을 잘해놨네.
D[i][j] = 정점 i에서 정점j로의 최소비용
AllPairsShortest(D[][])
FOR k in 1 -> n
FOR i in 1 -> n (단, i != k)
FOR j in 1 -> n (단, j!=k, j!=i)
D[i][j] <- min(D[i][k] + D[k][j], D[i][j])
import java.io.*;
import java.util.*;
class Main {
static int N, M;
static int[][] mat;
static final int INF = 100_000_001;
static StringBuilder sb = new StringBuilder();
public static void main(String[] args) throws Exception {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
N = Integer.parseInt(br.readLine()); // 도시의 수
M = Integer.parseInt(br.readLine()); // 버스의 수
mat = new int[N+1][N+1];
for (int n=1; n<=N; n++) {
Arrays.fill(mat[n], INF);
mat[n][n] = 0;
}
StringTokenizer st = null;
for (int m=0; m<M; m++) {
st = new StringTokenizer(br.readLine());
int a = Integer.parseInt(st.nextToken());
int b = Integer.parseInt(st.nextToken());
int c = Integer.parseInt(st.nextToken());
mat[a][b] = Math.min(mat[a][b], c);
}
floyd();
for (int i=1; i<=N; i++) {
for (int j=1; j<=N; j++) {
int value = mat[i][j];
sb.append(value == INF ? 0 : value).append(" ");
}
sb.append("\n");
}
System.out.println(sb);
}
static void floyd() {
for (int k=1; k<=N; k++) { // 경유지
for (int i=1; i<=N; i++) { // 출발지
for (int j=1; j<=N; j++) { // 도착지
if (mat[i][k] == INF || mat[k][j] == INF) continue; // 연결이 없는 경우 건너뜀
int s2e = mat[i][j]; // 기존 경로
int s2m2e = mat[i][k] + mat[k][j]; // 경유지를 거쳐간 경로
mat[i][j] = Math.min(s2e, s2m2e); // 업데이트
}
}
}
}
}
백준 - 플로이드(11404) 에 제출하면 정답처리가 된다.
- 인접행렬 출발지와 도착지가 본인이면 0, 외에는 INF로 초기화
- 인접행렬에 연결상태 입력
- floyd() 로직 실행
간단하게 모든 정점 사이의 최단 거리를 구할 수 있다.