题目大意:
在一张无向图上求一个不是自环的最小环
分析:
好难呀,如果只求最小环的权值我还勉强可以写出来,但是加上记录路径就完全不会了,觉得自己弱爆了O(≧口≦)O
先说怎么求最小环:
我们考虑这样一个问题,怎么求最短路?很简单Floyd、dijkstra,etc.,那么最小环呢?所以我们就要先考虑环和路径之间有什么关系,很明显,环是由路径连接而成的,详细来说,一个i~i的环可以拆成三条边i~k的直接路径,k~j的直接路径以及i~j的最短路径。为什么要这么拆呢?为什么不拆成两条最短路径或者三条最短路径呢?这就是Floyd的功劳了,我们考虑Floyd是怎么求最短路的,dis[i][j]=min(dis[i][k]+dis[k][j]),我们是通过枚举ij之间的中间点k来更新ij之间的最短路径,所以我们也可以通过枚举点k来更新ii之间的最短路径,也就是环,但是直接更新肯定不行,因为我们无法保证我们求出的ij之间的最短路不包含k,所以怎么办呢?我们可以强制要求k是环上编号最大的点,然后通过枚举k来更新最小环。
接下来就是很难想到的路径记录问题了:我们可以发现,一条从i到j的路径,如果看成有向边的话,每个点都有在这条路径上的唯一father节点,有了这个想法,这个问题就很好解决了,我们记录一下ij之间的最短路径上每个节点的pre值,在更新dis的时候,更新pre
代码如下:
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#define inf 0x7ffffff//注意inf不能开太小会WA太大会RE
using namespace std;
const int maxn=100+5;
int mp[maxn][maxn],n,m,path[maxn],cnt,pre[maxn][maxn],dis[maxn][maxn],ans;
inline int read(){
char ch=getchar();
int f=1,x=0;
while(!(ch>='0'&&ch<='9')){
if(ch=='-')
f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
x=x*10+ch-'0',ch=getchar();
return f*x;
}
signed main(void){
while(scanf("%d%d",&n,&m)!=EOF){
cnt=0,ans=inf;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
pre[i][j]=i,mp[i][j]=dis[i][j]=inf;
for(int i=1,x,y,z;i<=m;i++)
x=read(),y=read(),z=read(),dis[x][y]=dis[y][x]=mp[x][y]=mp[y][x]=min(mp[x][y],z);
for(int k=1;k<=n;k++){
for(int i=1;i<k;i++)
for(int j=i+1;j<k;j++)
if(ans>mp[i][k]+mp[k][j]+dis[i][j]){
ans=mp[i][k]+mp[k][j]+dis[i][j];//dis--最短距离,mp---直接距离
cnt=0;
int tmp=j;
while(tmp!=i){
path[++cnt]=tmp;
tmp=pre[i][tmp];
}
path[++cnt]=i,path[++cnt]=k;
}
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(dis[i][j]>dis[i][k]+dis[k][j]){
dis[i][j]=dis[i][k]+dis[k][j];
pre[i][j]=pre[k][j];
}
}
if(ans==inf)
printf("No solution.\n");
else{
for(int i=1;i<=cnt;i++)
cout<<path[i]<<" ";
cout<<endl;
}
}
return 0;
}
by >o< neighthorn