발상 & 풀이
Floyd - Warshall 알고리즘에 경로 역추적을 추가하는 문제다. 알고리즘 자체가 경로마다 모든 중간 정점을 끼워넣고 그 합계 비용을 검사하는 알고리즘이므로 경로 역추적도 저 중간 정점을 끼워넣은 새로운 경로가 최적일 때마다 그 경로로 갱신만 해주면 된다. 이전 문제들과 경로를 기록하는 구조가 달라서 조금 헷갈릴 수도 있지만 구조가 오히려 단순해서 직관적으로 생각하기는 더 쉽다.
코드
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
using namespace std;
struct eg {
int v = 1e5+1;
vector<int> p;
};
int main()
{
ios::sync_with_stdio(0); cin.tie(0);
int n, m; cin >> n >> m; vector<vector<eg>> dp(n + 1, vector<eg>(n + 1));
for (int i = 1; i <= m; i++) {
int s, e, c; cin >> s >> e >> c;
if (dp[s][e].v < c) continue;
dp[s][e].v = c;
dp[s][e].p = { s, e };
}
for (int k = 1; k <= n; k++) {
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
if (i == j || j == k || k == i) continue;
if (dp[i][j].v > dp[i][k].v + dp[k][j].v) {
dp[i][j].v = dp[i][k].v + dp[k][j].v;
dp[i][j].p = dp[i][k].p;
dp[i][j].p.insert(dp[i][j].p.end(), dp[k][j].p.begin() + 1, dp[k][j].p.end());
}
}
}
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
cout << (dp[i][j].v == 1e5 + 1 ? 0 : dp[i][j].v) << " ";
}
cout << "\n";
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
if (i == j || dp[i][j].p.size() == 0) {
cout << "0\n";
}
else {
cout << dp[i][j].p.size() << " ";
for (auto p : dp[i][j].p) {
cout << p << " ";
}
cout << "\n";
}
}
}
return 0;
}