版权声明:转载请在原文附上源连接以及作者,谢谢~ https://blog.csdn.net/weixin_39778570/article/details/87877664
ACM题集:https://blog.csdn.net/weixin_39778570/article/details/83187443
图论:https://blog.csdn.net/weixin_39778570/article
题目链接:http://poj.org/problem?id=1734
题目描述
给定一幅无向图,求大于等于3条边的最小环的路径。
分析
可以使用Folyd算法,枚举到第k阶段时,更新答案 ans = min(ans, d[i][j] + a[j][k] + a[k][j]),
再更新Folyd方程,表示前1~k-1个城市从i到j (j>i)的最小距离 + 从j经过k回到i,由此形成环。(只枚举不大于k的节点,由对称性可知是正确的,因为先枚举了大于k的点故中转点k小于i,j,和之后枚举i,k中转点为j是一样的,前则多次枚举i,j后达到最优,后者枚举也是,但是显然后者枚举的次数更少)
Code
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
#define ll long long
#define fo(i,j,n) for(register int i=j; i<=n; ++i)
using namespace std;
const int N = 105;
int n,m,a[N][N],d[N][N],pos[N][N],ans=0x3f3f3f3f;
vector< int > Path;
void get_path(int x, int y){
if(pos[x][y]==0)return;
get_path(x, pos[x][y]);
Path.push_back(pos[x][y]);
get_path(pos[x][y], y);
}
void solve(){
memcpy(d,a,sizeof(d));
for(int k=1; k<=n; k++){
for(int i=1; i<k; i++){ // 不包含k点,作为i,j的媒介点
for(int j=i+1; j<k; j++){
// 要装换成 long long 类型!!!,3个数相加
if(ans > (ll)d[i][j]+a[j][k]+a[k][i]){// i到j溜一遍,从j经过k回到i
ans = d[i][j]+a[j][k]+a[k][i];
Path.clear();
Path.push_back(i);
get_path(i,j); // 此时i,j间还没考虑到k这个点
Path.push_back(j);
Path.push_back(k); // 最后进过的点
}
}
}
// folyd第k阶段
for(int i=1; i<=n; i++){
for(int j=1; j<=n; j++){
if(d[i][j] > d[i][k]+d[k][j]){
d[i][j] = d[i][k]+d[k][j];
pos[i][j] = k;
}
}
}
}
if(ans==0x3f3f3f3f){
puts("No solution.");
}else{
for(int i=0; i<Path.size(); i++){
printf("%d ",Path[i]);
}
puts("");
}
}
int main(){
scanf("%d%d",&n,&m);
int x,y,z;
memset(a,0x3f,sizeof(a));
fo(i,1,n)a[i][i]=0;
fo(i,1,m){
scanf("%d%d%d",&x,&y,&z);
a[x][y] = a[y][x]= min(a[x][y],z);
}
solve();
return 0;
}