[Java] 플로이드 워샬
2024. 6. 13. 00:13ㆍAlgorithm/with Java
1. 플로이드 워샬이란?
- 가능한 모든 노드 쌍에 대해 최단 거리를 구하는 알고리즘.
- 시간복잡도 :
O(N^3)
Dijkstra와 달리
- 모든 노드 쌍에 대한 최단 거리를 구하고,
- 음의 가중치를 가지는 그래프에서도 쓸 수 있다!
2. 구현
DP로 접근하기 위해 부분 문제를 정의해야 한다.
s
에서e
까지 가는 데 걸리는 최단거리는(s->e)
또는(s->m→e)
이다.(s->m→e)
=s
에서m
까지 가는 데 걸리는 최단거리 +m
에서e
까지 가는 데 걸리는 최단거리
int INF = 1000000; // 도달할 수 없음을 의미 (구체적인 값은 문제 따라서 다름)
int[][] graph; // 그래프를 나타내는 2차원 배열
int n; // 노드의 개수
public void floydWarshall() {
// 모든 정점에서 모든 정점으로 가는 최소 비용을 INF로 초기화
for(int i=0; i<n; i++) {
Arrays.fill(graph[i], INF);
graph[i][i] = 0; // 자기 자신으로 가는 비용은 0
}
// 각 정점을 경유지로 가정하고 모든 정점 간의 최소 비용을 찾음
for(int k=0; k<n; k++) {
for(int i=0; i<n; i++) {
for(int j=0; j<n; j++) {
if(graph[i][k] + graph[k][j] < graph[i][j]) {
graph[i][j] = graph[i][k] + graph[k][j];
}
}
}
}
}
n
개의 노드를 가진 그래프에서 플로이드 워샬 알고리즘을 통해 각 노드 간의 최단 거리를 구하는 예시 코드다.
INF
는 무한을 의미하는 값으로 설정하였고, graph
는 그래프를 나타내는 2차원 배열입니다.
플로이드-워샬에서 핵심 아이디어는 경유지를 하나씩 추가해 가며 비용을 최적화하는 것이다.
마지막 경유지를 추가하는 시점에서는 모든 쌍에 대한 최단 거리를 알 수 있다.
점차 시작점에서 경유지로 가는 경로와 경유지에서 도착점으로 가는 경로의 거리가 점차 최적화된다.
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.StringTokenizer;
public class FloydWarshallTest {
static final int INF = 1000000;
static int[][] dp;
static int n, m; // 노드의 수, 간선의 수
public static void main(String[] args) throws Exception {
init();
solve();
}
static void init() throws Exception {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
n = Integer.parseInt(st.nextToken());
m = Integer.parseInt(st.nextToken());
dp = new int[n][n];
for(int i=0; i<n; i++) {
Arrays.fill(dp[i], INF);
dp[i][i] = 0;
}
int from ,to;
for(int i=0; i<m; i++) {
st = new StringTokenizer(br.readLine());
from = Integer.parseInt(st.nextToken());
to = Integer.parseInt(st.nextToken());
dp[from][to] = Integer.parseInt(st.nextToken()); // 간선 정보 입력
}
}
static void solve() {
for(int k=0; k<n; k++) { // 경유지
for(int i=0; i<n; i++) { // 시작점
for(int j=0; j<n; j++) { // 도착점
if(dp[i][k]+dp[k][j] < dp[i][j]) {
dp[i][j] = dp[i][k] + dp[k][j];
}
print(i, j);
}
}
}
}
static void print(int curY, int curX) {
StringBuffer sb = new StringBuffer();
for(int y=0; y<n; y++) {
for(int x=0; x<n; x++) {
if(y==curY && x==curX) sb.append("* ");
else if(dp[y][x] == INF) sb.append("~ ");
else sb.append(dp[y][x] + " ");
}
sb.append("\n");
}
System.out.println(sb);
}
}
4 6
0 1 3
0 2 6
1 2 2
1 3 1
2 3 5
2 0 3
for(int k=0; k<n; k++) { // 경유지
for(int i=0; i<n; i++) { // 시작점
for(int j=0; j<n; j++) { // 도착점
for문의 순서에 주의하자.
경유지부터 선택해야 한다.
알고리즘 | 용도 | 설명 | 표현 | 시간복잡도 | 기타 |
Prim | 최소 신장 트리 | 가중치가 있는 무방향 그래프에서 최소 신장 트리를 찾는 알고리즘. | 인접 리스트, 인접 행렬 | FOR : O(V^2) PQ : O(ElogE) |
무방향 |
Kruskal | 최소 신장 트리 | 가중치가 있는 무방향 그래프에서 최소 신장 트리를 찾는 알고리즘. | 간선 리스트 | O(ElogE + V) | 무방향 |
Dijkstra | 최소 경로 | 음의 가중치가 없는 그래프의 한 정점에서 다른 정점들까지의 최단 거리 비용을 구하는 알고리즘. | 인접 리스트, 인접 행렬 | FOR : O(V^2) PQ : O(ElogE) |
음의 가중치 X |
Bellman-Ford | 최소 경로 | 그래프의 한 정점에서 다른 정점들까지의 최단 거리 비용을 구하는 알고리즘. | 간선 리스트 | O(VE) | 음의 가중치 O |
Floyd-Warshall | 최소 경로 | 모든 정점 사이의 최단 거리 비용을 구하는 알고리즘. | 인접 행렬 | O(V^3) | 음의 가중치 O 단, 음수 사이클 X |
'Algorithm > with Java' 카테고리의 다른 글
풀었던 문제(0326 ~ 0404) (0) | 2024.06.21 |
---|---|
[Java] 문자열 패턴 매칭 (0) | 2024.06.13 |
[Java] LIS, LCS (0) | 2024.06.13 |
[Java] Knapsack (1) | 2024.06.12 |
[알고리즘] 2457. 공주님의 정원 (1) | 2024.03.05 |