#倍增,floyd#CF147B Smile House

题目

求一张有向图的最小正环(环上结点数最小)


分析

有环当且仅当 \(f[i][i]\) 为正数,

那么考虑跑 \(n\) 次 floyd 直接转移,时间复杂度为 \(O(n^4)\)

然而没必要这么做,因为这种转移具有结合律,

首先可以考虑最大的不满足上述条件的节点数加 1 即为答案。

那么可以直接处理出不超过 \(2^i\) 步的 \(f\) 数组,然后用倍增法拼凑即可


代码

#include <cstdio>
#include <cctype>
#include <cmath>
using namespace std;
const int N=311;
int n,m,mx,f[17][N][N],dp[N][N],g[N][N],ans;
int iut(){
	int ans=0,f=1; char c=getchar();
	while (!isdigit(c)) f=(c=='-')?-f:f,c=getchar();
	while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
	return ans*f;
}
int max(int a,int b){return a>b?a:b;}
int main(){
	n=iut(),m=iut(),mx=log(n)/log(2);
	for (int t=0;t<=mx;++t)
	for (int i=1;i<=n;++i)
	for (int j=1;j<=n;++j) f[t][i][j]=0xcfcfcfcf;
	for (int i=1;i<=n;++i)
	for (int j=1;j<=n;++j) dp[i][j]=0xcfcfcfcf;
	for (int i=1;i<=n;++i) f[0][i][i]=dp[i][i]=0;
	for (int i=1;i<=m;++i){
		int x=iut(),y=iut(),w0=iut(),w1=iut();
		f[0][x][y]=w0,f[0][y][x]=w1;
	}
	for (int t=1;t<=mx;++t)
	for (int k=1;k<=n;++k)
	for (int i=1;i<=n;++i)
	for (int j=1;j<=n;++j)
	    f[t][i][j]=max(f[t][i][j],f[t-1][i][k]+f[t-1][k][j]);
	for (int t=mx;~t;--t){
		for (int i=1;i<=n;++i)
		for (int j=1;j<=n;++j) g[i][j]=0xcfcfcfcf;
		for (int k=1;k<=n;++k)
		for (int i=1;i<=n;++i)
		for (int j=1;j<=n;++j)
		    g[i][j]=max(g[i][j],dp[i][k]+f[t][k][j]);
		int flag=0;
		for (int i=1;i<=n;++i)
		    if (g[i][i]) {flag=1; break;}
		if (flag) continue;
		for (int i=1;i<=n;++i)
		for (int j=1;j<=n;++j) dp[i][j]=g[i][j];
		ans|=1<<t;
	}
	if (ans>=n) printf("0");
	    else printf("%d",ans+1);
	return 0;
}
posted @ 2021-11-18 14:26  lemondinosaur  阅读(51)  评论(0)    收藏  举报