Description
在忘记考虑负环之后,黎瑟的算法又出错了。对于边带权的有向图 G = (V, E),请找出一个点数最小的环,使得
环上的边权和为负数。保证图中不包含重边和自环。
Input
第1两个整数n, m,表示图的点数和边数。
接下来的m行,每<=三个整数ui, vi, wi,表<=有一条从ui到vi,权值为wi的有向边。
2 <= n <= 300
0 <= m <= n(n <= 1)
1 <= ui, vi <= n
|wi| <= 10^4
Output
仅一行一个整数,表示点数最小的环上的点数,若图中不存在负环输出0。
对每个点加入边权0的自环后,答案变得满足二分性质,于是可以用倍增floyd处理出两两点间走2^k步的最短路,并二分出最大的不存在负环的步数,从而得出答案
时间复杂度O(n3logn)
#include<bits/stdc++.h> const int inf=0x3f3f3f3f; int n,m; int f[9][307][307],g[307][307],h[307][307]; void mins(int&a,int b){if(a>b)a=b;} int main(){ scanf("%d%d",&n,&m); memset(f,0x3f,sizeof(f)); for(int i=1;i<=n;++i)f[0][i][i]=0; while(m--){ int a,b,c; scanf("%d%d%d",&a,&b,&c); mins(f[0][a][b],c); } for(int i=0;i<8;++i){ for(int a=1;a<=n;++a) for(int c=1;c<=n;++c) for(int b=1,x=f[i][a][c],*y=f[i][c],*z=f[i+1][a];b<=n;++b) mins(z[b],x+y[b]); } bool is=0; for(int a=1;a<=n;++a)if(f[8][a][a]<0)is=1; if(!is)return puts("0"),0; int ans=0; memset(g,0x3f,sizeof(g)); for(int a=1;a<=n;++a)g[a][a]=0; for(int i=8;i>=0;--i){ memset(h,0x3f,sizeof(h)); for(int a=1;a<=n;++a) for(int c=1;c<=n;++c) for(int b=1,x=f[i][a][c],*y=g[c],*z=h[a];b<=n;++b) mins(z[b],x+y[b]); is=0; for(int a=1;a<=n;++a)if(h[a][a]<0)is=1; if(!is){ ans|=1<<i; memcpy(g,h,sizeof(g)); } } printf("%d",ans+1); return 0; }