浴谷夏令营例题...讲师讲的很清楚,没看题解代码就自己敲出来了
f[l][i][j]表示i到j走2^l条边的最短距离,显然有f[l][i][j]=min(f[l][i][j],f[l-1][i][k]+f[l-1][k][j])。
是否有负环可以用f[l][i][i]是否<0来判,我们从高位往低位贪心,找到走的边数最大的没有负环的图,把最大走的边数+1就必定有负环,也就是答案了。
#include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> #include<cmath> #include<algorithm> using namespace std; const int maxn=310,inf=1e9; int n,m,x,y,ans; int f[10][maxn][maxn],g[maxn][maxn],h[maxn][maxn]; inline void read(int &k) { int f=1;k=0;char c=getchar(); while(c<'0'||c>'9')c=='-'&&(f=-1),c=getchar(); while(c<='9'&&c>='0')k=k*10+c-'0',c=getchar(); k*=f; } int main() { read(n);read(m);int L=(int)ceil(log(n)/log(2)); for(int i=1;i<=n;i++)for(int j=1;j<=n;j++) { for(int k=0;k<=L;k++)f[k][i][j]=(i-j||k)?inf:0; g[i][j]=(i-j)?inf:0; } for(int i=1;i<=m;i++)read(x),read(y),read(f[0][x][y]); for(int l=1;l<=L;l++) for(int k=1;k<=n;k++) for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) f[l][i][j]=min(f[l][i][j],f[l-1][i][k]+f[l-1][k][j]); int FLAG=0; for(;L>=0;L--) { for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)h[i][j]=(i-j)?inf:0; int flag=0; for(int k=1;k<=n;k++) { for(int i=1;i<=n;i++) { for(int j=1;j<=n;j++) h[i][j]=min(h[i][j],f[L][i][k]+g[k][j]); if(h[i][i]<0){flag=1;FLAG=1;break;} } if(flag)break; } if(flag)continue; for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)g[i][j]=h[i][j]; ans+=1<<L; } printf("%d",FLAG?ans+1:0); }