观光之旅(Floyd求最小环)

题目描述

给定一张无向图,求图中一个至少包含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;
}

猜你喜欢

转载自blog.csdn.net/li_wen_zhuo/article/details/109660514