题解 P6175 【无向图的最小环问题】

这题竟然没有题解,那我就来发一篇吧。

第一眼看到这题:最小环?什么鬼!用 SPFA 好像很麻烦欸。然后一看数据:(1leq nleq 100)。好吧这题用邻接矩阵和 floyd 就能过。

floyd 是一种动态规划求最短路径的方法,代码极短,并且很好理解(代价就是在最短路径算法中无人能敌的 (Theta(n^3)) 的时间复杂度)。那么最短环怎么办呢,只要稍微改一下就行了!

这是一个正常的 floyd 的核心代码(无向图):

for(rg int k=1;k<=n;++k)  //经不经过 k。
{
	for(rg int i=1;i<=n;++i) //从 i 点。
	{
		for(rg int j=1;j<=n;++j)  //到 j 点。
		{
			if(f[i][j]>f[i][k]+f[k][j])  //如果经过 k 更短的话更新。 
				f[i][j]=f[i][k]+f[k][j];
			f[j][i]=f[i][j];  //无向图要存双向。
		}
	}
}

但是我们要让它为环,怎么办呢?那就在找完最短路之后再加一个循环找回路!(简单粗暴)

我们可以用一个邻接矩阵存下这张图,如果两点不联通就为无限大(我们这里就用一个很大的数字代替无限大),所以 (f_{i,j}+a_{i,k}+a_{k,j}) 即为一个环的权值。穷举 (i,j,k) 不断更新答案就行了。

for(rg int k=1;k<=n;++k)
{
	for(rg int i=1;i<k;++i)
	{
		for(rg int j=i+1;j<k;++j)
		{   //这里的 a 是邻接矩阵存图。f 数组就是最短路。
			if(ans>f[i][j]+a[i][k]+a[k][j])   //因为 a 的初始值为 1e8,就省去判断。 
				ans=f[i][j]+a[i][k]+a[k][j];  //更新答案。 
		}
	}
	for(rg int i=1;i<=n;++i)
	{
		for(rg int j=1;j<=n;++j)
		{
			if(f[i][j]>f[i][k]+f[k][j])  //如果更短的话更新。 
				f[i][j]=f[i][k]+f[k][j];
			f[j][i]=f[i][j];
		}
	}
}

最后提醒:要注意这是无向图,对于每次输入要存正反两次。

完整无注释代码:

#include<bits/stdc++.h>
using namespace std;
#define maxn 110
#define inf 1e8
#define rg register
int n,m,u,v,w,ans=inf;
int a[maxn][maxn],f[maxn][maxn];
int main()
{
	cin>>n>>m;
	for(rg int i=1;i<=n;++i)
	{	
		for(rg int j=1;j<=n;++j)
		{	
			if(i!=j)
			{	
				a[i][j]=inf;
				f[i][j]=inf;
			}
		}
	}
	for(rg int i=1;i<=m;++i)
	{
		cin>>u>>v>>w;
		a[u][v]=w;
		a[v][u]=w;
		f[u][v]=w;
		f[v][u]=w;
	}
	for(rg int k=1;k<=n;++k)
	{
		for(rg int i=1;i<k;++i)
		{
			for(rg int j=i+1;j<k;++j)
			{
				if(ans>f[i][j]+a[i][k]+a[k][j])
					ans=f[i][j]+a[i][k]+a[k][j];
			}
		}
		for(rg int i=1;i<=n;++i)
		{
			for(rg int j=1;j<=n;++j)
			{
				if(f[i][j]>f[i][k]+f[k][j])
					f[i][j]=f[i][k]+f[k][j];
				f[j][i]=f[i][j];
			}
		}
	}
	if(ans==inf)	
		cout<<"No solution."<<endl;
	else	
		cout<<ans<<endl;
	return 0;
}

其实代码很短的,只是个人码风会显得长。

原文地址:https://www.cnblogs.com/win10crz/p/12859754.html