题目链接:https://www.acwing.com/problem/content/346/
题目大意:确定无向带权图上至少包含 3 个节点的最小环
solution
一道无向图上的最小环问题 , 考虑 (Floyd) , 设 (i) 到 (j) 间的道路长为 (f[i][j]) , 最短路径长 (g[i][j]) , 若每次 (Floyd) 最外层 遍历到 (k) 时 , (g[i][j]) 决定了 (i) 到 (j) 间经过节点编号不超过 (k) 的最短路径 , 不妨以 (k) 为环上的节点 , 枚举 (i) 和 (j) , 即是枚举 (k) 在环上的相邻节点 , 那么这个环的最小长度为(f[i][k] + f[k][j] + g[i][j]) , 这样就可以找出环上节点编号不超过 (k) 的最小环 , 由对称性 , 最终可以遍历到每一个最小环
拓展 : 对于有向图上的最小环 , 可以依次枚举起点 (s) , 再在 (dijstra) 中 (s) 向外拓展后 , 把 (shortestpath[s]) 设为极大值 , 最终的 (shortestpath[s]) 即为从 (s) 开始的最小环的长度 , 笔者在这不多加赘述
时间复杂度 : (O(n^3))
code
#include<bits/stdc++.h>
using namespace std;
template <typename T> inline void read(T &FF) {
int RR = 1; FF = 0; char CH = getchar();
for(; !isdigit(CH); CH = getchar()) if(CH == '-') RR = -RR;
for(; isdigit(CH); CH = getchar()) FF = FF * 10 + CH - 48;
FF *= RR;
}
inline void file(string str) {
freopen((str + ".in").c_str(), "r", stdin);
freopen((str + ".out").c_str(), "w", stdout);
}
const int N = 105;
int g[N][N], ans = INT_MAX, f[N][N];
int pos[N][N], n, m;
queue<int> path;
void get_path(int xi, int yi) {
if(pos[xi][yi] == 0) return;
get_path(xi, pos[xi][yi]);
path.push(pos[xi][yi]);
get_path(pos[xi][yi], yi);
}
int main() {
//file("");
int u, v, w;
read(n), read(m);
memset(g, 0x3f, sizeof(g));
memset(f, 0x3f, sizeof(f));
for(int i = 1; i <= n; i++) f[i][i] = g[i][i] = 0;
for(int i = 1; i <= m; i++) {
read(u), read(v), read(w);
g[u][v] = g[v][u] = min(g[u][v], w);
f[u][v] = f[v][u] = g[u][v];
}
ans = f[0][0];
for(int k = 1; k <= n; k++) {
for(int i = 1; i < k; i++)
for(int j = i + 1; j < k; j++)
if((long long)g[i][j] + f[i][k] + f[k][j] < ans) {
ans = g[i][j] + f[i][k] + f[k][j];
while(!path.empty()) path.pop();
path.push(i);
get_path(i, j);
path.push(j); path.push(k);
}
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++)
if(g[i][k] + g[k][j] < g[i][j]) {
g[i][j] = g[i][k] + g[k][j];
pos[i][j] = k;
}
}
if(path.empty()) {
puts("No solution.");
return 0;
}
while(!path.empty()) cout << path.front() << " ", path.pop();
cout << endl;
return 0;
}