모든 정점 쌍 사이의 최단 경로를 구하는 알고리즘입니다. 동적 계획법(DP)을 사용.
“A에서 B로 가는 경로에 K를 거쳐가면 더 짧아질까?”를 모든 정점에 대해 확인.
dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j])
핵심: k를 거쳐가는 것이 더 나은지 비교
// 1. 초기화: 인접 행렬로 그래프 표현
int[][] dist = new int[n][n];
for (int i = 0; i < n; i++) {
Arrays.fill(dist[i], INF);
dist[i][i] = 0; // 자기 자신은 0
}
// 간선 정보 입력
for (int[] edge : edges) {
dist[edge[0]][edge[1]] = edge[2]; // 가중치
}
// 2. 플로이드-워셜 핵심 로직
for (int k = 0; k < n; k++) { // 경유지
for (int i = 0; i < n; i++) { // 출발지
for (int j = 0; j < n; j++) { // 도착지
if (dist[i][k] + dist[k][j] < dist[i][j]) {
dist[i][j] = dist[i][k] + dist[k][j];
}
}
}
}
for (int k = 0; k < n; k++) // "k번 정점을 거쳐갈까?"
for (int i = 0; i < n; i++) // "i에서 출발해서"
for (int j = 0; j < n; j++) // "j로 가는데"
중요: k가 가장 바깥 루프여야 합니다! 이것이 DP의 핵심이다.
그래프:
1 → 2 (3)
2 → 3 (4)
1 → 3 (10)
초기 dist:
1 2 3
1 0 3 10
2 INF 0 4
3 INF INF 0
k=1 (1번 정점 경유):
1 2 3
1 0 3 10
2 INF 0 4
3 INF INF 0
(변화 없음)
k=2 (2번 정점 경유):
1 2 3
1 0 3 7 ← dist[1][3] = min(10, 3+4) = 7
2 INF 0 4
3 INF INF 0
k=3 (3번 정점 경유):
1 2 3
1 0 3 7
2 INF 0 4
3 INF INF 0
(변화 없음)
최종 결과: 1→3 최단거리 = 7
public int[][] floydWarshall(int n, int[][] edges) {
int[][] dist = new int[n + 1][n + 1];
for (int i = 1; i <= n; i++) {
Arrays.fill(dist[i], INF);
dist[i][i] = 0;
}
for (int[] edge : edges) {
dist[edge[0]][edge[1]] = edge[2];
}
for (int k = 1; k <= n; k++) {
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
if (dist[i][k] != INF && dist[k][j] != INF) {
dist[i][j] = Math.min(dist[i][j],
dist[i][k] + dist[k][j]);
}
}
}
}
return dist;
}
public boolean[][] findReachability(int n, int[][] edges) {
boolean[][] reach = new boolean[n + 1][n + 1];
// 자기 자신은 도달 가능
for (int i = 1; i <= n; i++) {
reach[i][i] = true;
}
// 직접 연결된 간선
for (int[] edge : edges) {
reach[edge[0]][edge[1]] = true;
}
// 전이적 폐포 (Transitive Closure)
for (int k = 1; k <= n; k++) {
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
if (reach[i][k] && reach[k][j]) {
reach[i][j] = true;
}
}
}
}
return reach;
}
// 플로이드-워셜 실행 후
for (int i = 1; i <= n; i++) {
if (dist[i][i] < 0) {
System.out.println("음수 사이클 존재!");
break;
}
}
| 알고리즘 | 시간 복잡도 | 용도 | 음수 간선 |
|---|---|---|---|
| 플로이드-워셜 | O(n³) | 모든 쌍 최단 경로 | ✓ (사이클 X) |
| 다익스트라 | O((V+E)logV) | 단일 출발점 | ✗ |
| 벨만-포드 | O(VE) | 단일 출발점 | ✓ |
// Win 관계를 boolean으로 표현
boolean[][] win = new boolean[n + 1][n + 1];
// 직접 경기 결과
for (int[] result : results) {
win[result[0]][result[1]] = true;
}
// 전이적 관계 추론
for (int k = 1; k <= n; k++) {
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
// i→k이고 k→j이면, i→j도 성립
if (win[i][k] && win[k][j]) {
win[i][j] = true;
}
}
}
}