题目描述
给定一张无向图,求图中一个至少包含3个点的环,环上的节点不重复,并且环上的边的长度之和最小。
该问题称为无向图的最小环问题。
你需要输出最小环的方案,若最小环不唯一,输出任意一个均可。
输入格式
第一行包含两个整数N和M,表示无向图有N个点,M条边。
接下来M行,每行包含三个整数u,v,l,表示点u和点v之间有一条边,边长为l。
输出格式
输出占一行,包含最小环的所有节点(按顺序输出),如果不存在则输出’No solution.’。
数据范围
1≤N≤100,
1≤M≤10000,
1≤l<500
输入样例
5 7
1 4 1
1 3 300
3 1 10
1 2 16
2 3 100
2 5 15
5 3 20
输出样例
1 3 5 2
题目分析
要想用Floyd算法求最小环。我们首先要清楚Floyd算法的原理:
最外层循环k表示:只用最大不超过k的节点。后面的两层循环i,j分别表示:起点和终点。
假设最外层循环循环到k的时候,一开始d[][](邻接矩阵)中存储的是:所有点对(i,j),即所有的从 i 到 j,且只经过1-(k-1)这k-1个点的最短路径。有了所有这样的路径之后,我们就可以求出图中所有最大节点为k的环了求法为:环的长度=d[i][j]+h[i][k]+h[k][j] //从i到j,只用最大不超过k-1的节点的最短路径+原图中i-k这条边的长度+k-j这条边的长度.
通过这种方式找出所有环,并找出长度最小的环,即为该图的最小环。
不过这道题并没有让我们求最小环的长度,而是求最小环的路径。而这也是本题的另外一个难点。
为了求出最小环的路径,我们首先要将Floyd算法的路径保存下来。
定义pos[i][j],表示:从i-j的最短路径中,经过的点的最大编号为pos[i][j](pos[i][j]==0 表示i-j的最短路径就是i-j这一条边)。其求法也是要根据Floyd算法的原理来考虑
通过这个数组我们就可以求出最小环求法中d[i][j]所包含的路径了,然后再把剩下的三个点i,j,k加上即可得到最小环的路径了。
最后输出该路径即可。
代码如下
#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <map>
#include <queue>
#include <vector>
#include <set>
#include <algorithm>
#define LL long long
#define ULL unsigned long long
#define PII pair<int,int>
#define x first
#define y second
using namespace std;
const int N=105,INF=0x3f3f3f3f;
int h[N][N],d[N][N]; //h[][]存原图,d[][]存Floyd处理过的图
int pos[N][N];
int path[N],cnt; //存最小环的节点
void getPath(int i,int j) //获取i->j最短路上的点
{
if(!pos[i][j]) return; //如果i到j路径上没有中间点,则直接退出
int k=pos[i][j]; //获取i到j路径上的一点k
getPath(i,k); //递归求i->k上的点
path[cnt++]=k; //放入中间点k
getPath(k,j); //递归求k->j上的点
}
int main()
{
memset(h,0x3f,sizeof h);
int n,m;
scanf("%d %d",&n,&m);
while(m--) //建图
{
int u,v,w;
scanf("%d %d %d",&u,&v,&w);
h[u][v]=h[v][u]=min(h[u][v],w);
}
memcpy(d,h,sizeof h); //首先将h复制到d
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((LL)d[i][j]+h[i][k]+h[k][j]<res) //如果当前环长度小于当前最小值
{
res=d[i][j]+h[i][k]+h[k][j]; //则更新res并求出该路径
cnt=0;
path[cnt++]=k;
path[cnt++]=i;
getPath(i,j);
path[cnt++]=j;
}
for(int i=1;i<=n;i++) //Floyd算法求最短路
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(res>=INF) puts("No solution.");
else { //若有解,则输出路径
for(int i=0;i<cnt;i++)
printf("%d ",path[i]);
}
return 0;
}