无向图的最小环问题

一道很有意思的题。

算法要素:floyd+利用过程量

核心思想:算法可以利用的部分不只有结果量,过程量也会起到一些意想不到的作用。

洛谷题面传送门

题目分析:

一、先说Floyd的本质:
用多了f[i][j]的定义,就会忘了其实它是压掉一维的结果。

最初的定义是f[i][j][k],意为从i到j,只经过1~k之间的点,最短路是多少。
因此转移式为:
f[i][j][k]=min(f[i][j][k-1],f[i][k][k-1]+f[k][j][k-1])。

很容易发现:每一次k只会从k-1转移而来,因此可以压缩一维变成f[i][j]。

二、floyd在这道题中的灵活应用:
每次只用k更新f[i][j],并统计形成的最小环。

--所以为什么每次只用k作为中转点更新f[i][j]?
很简单,因为如果所有值都更新完,则f[i][j]可能包括了点k,
不能保证f[i][j]、a[j][k]、a[k][i]在原图中均呈现为链的形式。
emm可能没说明白,那么就直接看代码吧。

还有一个奇怪的bug:
如果初值给的太大,有爆int的情况。
所以要注意初值如果赋成1e9+7,而且会出现1e9+7+1e9+7+一个其他值,那么就有可能爆int。

Code

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e2+30;
long long f[maxn][maxn],a[maxn][maxn],ans=1e9+7;
int n,m;
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;++i)
		for(int j=1;j<=n;++j)
			if(i!=j) a[i][j]=f[i][j]=1e9+7;
	for(int i=1;i<=m;++i)
	{
		int x,y;
		long long w;
		scanf("%d%d%lld",&x,&y,&w);
		a[x][y]=a[y][x]=f[x][y]=f[y][x]=w;	
	}
	for(int k=1;k<=n;++k)
	{
		for(int i=1;i<k;++i)
			for(int j=i+1;j<k;++j)
				ans=min(ans,f[i][j]+a[j][k]+a[k][i]);
		for(int i=1;i<=n;++i)
			for(int j=1;j<=n;++j)
				f[i][j]=min(f[i][j],f[i][k]+f[k][j]),f[j][i]=f[i][j];
	}
	if(ans==1e9+7) printf("No solution.\n");
	else printf("%lld\n",ans);
	return 0;
}
上一篇:【字符串】字符串多项式哈希 - 第2节


下一篇:2021牛客暑期多校训练营4 J.Average (二分答案,前缀和维护动态区间)