Floyd求无向图最小环
算法思想
如果若干个点形成一个环,则该环对应的有限点集V一定含有最大编号的点Kmax,按编号小到大枚举这个最大点k。
枚举时,以k为外层循环,每层循环考虑:
只经过前k-1个点的i,j间最短路径d[i][j],连接i,k的边g[i][k],连接k,j的边g[k][j],若d[i][j]+g[i][k]+g[k][j]<mn,则更新最小环的长度,同时记录路径,该环路径包含了曾经的k-1个点中的最短路上的点,也包括i,j,k三点,放入答案数组中即可。
若要统计最小环的个数,则可以在mn==d[i][j]+g[i][k]+g[k][j]时计数,要注意,如果mn在后续被更新,该计数个数要重置。
变量定义
d[i][j]:从i到j的最短路径长
g[i][j]:连接i和j的最小边长(直接相连)
pos[i][j]:在Floyd中更新了i,j间最短路径的点
mn: 最小环的长度
代码 POJ 1734(模板题)
#include<cstdio> #include<iostream> #include<vector> using namespace std; const int maxn=101; const int _INF=0x7fffffff; const int INF=_INF/3; int n,m; int d[maxn][maxn],g[maxn][maxn],pos[maxn][maxn]; int id,mn; int path[maxn],cnt; void floyd() { mn=INF; for(int k=1;k<=n;k++) { for(int i=1;i<k;i++) for(int j=i+1;j<k;j++) { if(mn>d[i][j]+g[i][k]+g[k][j]) { mn=d[i][j]+g[i][k]+g[k][j]; cnt=0; int t=i; while(t!=j) { path[++cnt]=t; t=pos[j][t]; } path[++cnt]=j; path[++cnt]=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]) { pos[i][j]=pos[k][j];//无向图,每次更新pos[i][j]即可(当循环至j=i,i=j时,pos[j][i]=pos[k][i] ) d[i][j]=d[i][k]+d[k][j]; } } } } void init() { for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) { pos[i][j]=i; if(i!=j) g[i][j]=d[i][j]=INF; else g[i][j]=d[i][j]=0; } } int main() { scanf("%d%d",&n,&m); init(); for(int i=1;i<=m;i++) { int x,y,c; scanf("%d%d%d",&x,&y,&c); if(c<g[x][y]) d[x][y]=d[y][x]=g[x][y]=g[y][x]=c; } floyd(); if(mn==INF) printf("No solution.\n"); else { for(int i=1;i<=cnt;i++) printf("%d ",path[i]); } return 0; }