BOJ 11780. 플로이드2
1. 문제설명
N
개의 정점과M
개의 간선으로 이뤄진 그래프가 있다.- 모든 정점의 쌍(A, B)에 대해 A에서 B로 가는 비용의 최소값을 구하라.
-
추가로, 어떤 정점
i
에서j
로 가는 경로를 출력하라 - 문제 분류
- Graph, Floyd
2. 알고리즘 설계
- 플로이드-워셜 알고리즘의 경로 복원 문제이다.
- 생각보다 극악의 난이도였다…
- 우선, 문제의 첫번째 출력은 플로이드-워셜 값을 출력해주면 된다.
- 경로 복원
- 초기화는 간선
a->b
에 대해 다음 위치는b
로 저장한다. - 이후, 플로이드-워셜 값이 갱신될 때(
i->j
보다i->k->j
가 더 비용이 적을 때)i->j
로 갈 때의 다음 위치인next[i][j]
를next[i][k]
의 값으로 저장한다. - 출력은 어떻게 하면 될까?
i->j
경로이므로,i
를 출력한다.- 이후에는 next 배열의 값을 쭉 따라가면 된다.
- 첫 갱신은
next[cur][j]
가 된다.- 즉, 현재 위치에서 j로 가는데, 어떤 노드로 이동하는가?
- 두번째 갱신은 풀어보면
next[next[cur][j]][j]
가 된다.- 즉, 이전에 이동한 위치에서 j로 가는 경로를 출력하게 된다.
- 이런식으로 이동을 하다가, 다음 위치가 j랑 같아지면 만나게 된 것이다.
- 첫 갱신은
cur == j
가 탈출 조건이므로, 마지막에j
를 경로에 추가시키면 된다.
- 초기화는 간선
i==j
즉, 자신에게 가는 경로 또는i->j
로 가는 길이 없는 경우는 경로가 없다
3. 전체 코드
#include <bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f;
int a, b, c, n, m;
int arr[102][102], nxt[102][102];
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
cin >> n >> m;
for(int i=1; i<=n; i++)
for(int j=1; j<=n; j++)
arr[i][j] = (i==j ? 0 : INF);
while(m--) {
cin >> a >> b >> c;
arr[a][b] = min(arr[a][b], c);
nxt[a][b] = b;
}
for(int k=1; k<=n; k++)
for(int i=1; i<=n; i++)
for(int j=1; j<=n; j++) {
if(arr[i][j] > arr[i][k] + arr[k][j]) {
arr[i][j] = arr[i][k] + arr[k][j];
nxt[i][j] = nxt[i][k];
}
}
for(int i=1; i<=n; i++) {
for(int j=1; j<=n; j++)
cout << (arr[i][j] == INF ? 0 : arr[i][j]) << ' ';
cout << '\n';
}
for(int i=1; i<=n; i++) {
for(int j=1; j<=n; j++) {
if(arr[i][j] == 0 || arr[i][j] == INF) {
cout << "0\n";
continue;
}
vector<int> path;
int cur = i;
while(cur != j) {
path.push_back(cur);
cur = nxt[cur][j];
}
path.push_back(j);
cout << path.size() << ' ';
for(int x : path) cout << x << ' ';
cout << '\n';
}
}
return 0;
}