观光之旅 Floyd 求最小环
题目
思路
设路径\(u_i \rightarrow u_w \rightarrow u_j \rightarrow \dots \rightarrow u_i\) 表示环\(S\), 其中\(w\)表示路径中编号最大的点,且\(i\)和\(j\) 是直接连接\(w\)点,那么任意的环都可以表示为\(C(i, w, j)\).
我们假定\(d(i, j)\)为\(u_i \rightarrow \dots \rightarrow u_j\)的最短路径且只经过编号为\([1, w)\)点,\(v(i, j)\) 为\(i\)和\(j\)的直接边权,那么所有环的最小权值可以表示成为\(d(i, j) + v(i, w) + v(w, j)\)。
利用上述性质,我们可以使用\(Floyd\)算法,在\(Floyd\)算法中\(d(i, j)\)表示了经过编号为\([1, w)\)内的点的最小边权,因此我们可以在算法中每次枚举\(w\)来求最小环。另外求最小环的路径的时候,应该在\(Floyd\)算法中求,因为只经过\([1, w)\)内的点。
Code
#include <bits/stdc++.h>
using i64 = long long;
int n, m, ans, ti, tj, tm;
int g[110][110], val[110][110];
int p[110][110];
std::vector<int> res;
void path(int i, int j) {
if (!p[i][j]) return;
int k = p[i][j];
path(i, k);
// std::cout << k << " ";
res.push_back(k);
path(k, j);
}
void floyd() {
ans = 0x3f3f3f3f;
memcpy(val, g, sizeof g);
for (int k = 1; k <= n; k ++) {
for (int i = 1; i < k; i ++) {
for (int j = 1; j < i; j ++) {
// ans = std::min(ans, g[i][j] + val[i][k] + val[k][j]);
if (ans > g[i][j] + val[i][k] + val[k][j]) {
ti = i, tj = j, tm = k;
ans = g[i][j] + val[i][k] + val[k][j];
res.clear();
res.push_back(i);
res.push_back(k);
res.push_back(j);
path(j, i);
}
}
}
for (int i = 1; i <= n; i ++) {
for (int j = 1; j <= n; j ++) {
// g[i][j] = std::min(g[i][j], g[i][k] + g[k][j]);
if (g[i][j] > g[i][k] + g[k][j]) {
g[i][j] = g[i][k] + g[k][j];
p[i][j] = k;
}
}
}
}
}
int main() {
std::cin >> n >> m;
for (int i = 1; i <= n; i ++) {
for (int j = 1; j <= n; j ++) {
g[i][j] = (i == j)?0:0x3f3f3f3f;
}
}
while (m --) {
int a, b, c;
std::cin >> a >> b >> c;
g[a][b] = g[b][a] = std::min(c, g[a][b]);
}
floyd();
if (ans == 0x3f3f3f3f) {
return puts("No solution."), 0;
}
for (int i = 0; i < res.size(); i ++) {
std::cout << res[i] << " \n"[i == (int) res.size() - 1];
}
}