题目描述
找到节点大于等于三个的长度最小的环。
没有 no solution 有输出任意路径
有一个关键的性质,当用k更新i到j的距离的时候,k的节点一定大于i和j
这里给出证明:
当有i j k 三个点的时候,如果i < k < j, 那么当枚举i j 中间k节点的时候,已经在枚举 i到k 的时候, ijk已经更新过距离了, 这里不会更新距离。 所以当更新距离的时候,一定是k是最大的。
比较难理解。我也表达的不清楚…多担待
C++ 代码
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn = 102;
const ll INF = 0x3f3f3f3f;
int n, m;
int g[maxn][maxn], d[maxn][maxn], pos[maxn][maxn];
int path[maxn], idx;
void get_path(int i, int j) // 递归得到路径,从i到j的路径
{
if(pos[i][j] == 0) return; // 如果i到j之间没有点,那么直接返回
int k = pos[i][j]; // 得到i到j之间的一个点
get_path(i, k); // 递归
path[idx ++] = k; // 将中间的点放入数组
get_path(k, j);
}
int main()
{
cin >> n >> m;
memset(g, 0x3f, sizeof g);
for(int i = 1; i <= n; i ++) g[i][i] = 0;
for(int i = 1; i <= m; i ++)
{
int a, b, c;
cin >> a >> b >> c;
g[a][b] = g[b][a] = min(g[a][b], c); // 没说有无重复边
}
memcpy(d, g, sizeof g);
int res = INF;
for(int k = 1; k <= n; k ++)
{
for(int i = 1; i < k; i ++)
for(int j = i+1; j < k; j ++)
if(res > (ll)g[i][j] + d[j][k] + d[k][i]) // 如果floyed更新的i到j 与原来i到k,k到j比res小
{
res = g[i][j] + d[j][k] + d[k][i]; // 更新res
idx = 0;
path[idx ++] = k;
path[idx ++] = i;
get_path(i, j);
path[idx ++] = j;
}
for(int i = 1; i <= n; i ++) // 继续用floyed来更新节点之间的距离。
for(int j = 1; j <= n; j ++)
if(g[i][j] > g[i][k] + g[k][j])
{
g[i][j] = g[i][k] + g[k][j];
pos[i][j] = k;
}
}
if(res == INF) puts("No solution.");
else
{
for(int i = 0; i < idx; i ++)
{
cout << path[i] << " ";
}
cout << endl;
}
return 0;
}