题意
给你 个点, 条边的无向图,但一条无向边的两个方向的边权不同,求图上最小正环的大小。
分析
考虑 转移式,设 表示走了不超过 条边,从 到 的最大边权(走不通为极小值)。
那么转移为:
在我们计算答案时,我们考虑使用倍增。
先尝试走了不超过 条边从 走到了 的最小边权和, 从大到小枚举。
1.如果没有正环,说明答案大于 ,那么下一步增加 条边。
转移时需用到上一次状态,用 记录上一次尝试 的最长路径。
2.如果有正环,说明答案小于等于 ,那么下一步尝试 条边。
代码
#include<bits/stdc++.h> #define ll long long const int N=300+5,M=10,INF=0x3f3f3f3f,mod=998244353; using namespace std; int n,m,x,y,z,w,ans; int dp[M+5][N][N]; int tmp[N][N] ,now[N][N]; inline int read() { register int x=0,f=1;char c=getchar(); while(c<'0'||c>'9'){if(c=='-') f=-1;c=getchar();} while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar(); return x*f; } int qpow(int a,int b) { int ans=1; while(b){if(b&1) ans=ans*a%mod;a=a*a%mod;b>>=1;} return ans; } int main() { memset(now,0xcf,sizeof(now)); memset(dp,0xcf,sizeof(dp)); n=read();m=read(); for(int i=1;i<=m;i++) { int x=read(),y=read(),z=read(),w=read(); dp[0][x][y]=z;dp[0][y][x]=w; } for(int i=1;i<=n;i++) now[i][i]=0,dp[0][i][i]=0; for(int s=1;s<=M;s++) for(int k=1;k<=n;k++) for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) dp[s][i][j]=max(dp[s][i][j],dp[s-1][i][k]+dp[s-1][k][j]); for(int s=M;s>=0;s--) { memset(tmp,0xcf,sizeof(tmp)); for(int k=1;k<=n;k++) for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) tmp[i][j]=max(tmp[i][j],now[i][k]+dp[s][k][j]); bool f=0; for(int i=1;i<=n;i++) if(tmp[i][i]>0) {f=1;break;} if(f) continue; else { for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) now[i][j]=tmp[i][j]; ans+=1<<s; } } printf("%d",ans>=n?0:ans+1); return 0; }