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;
}