BZOJ4773: 负环
n<=300个点的有向图求点数最少的负环。
先倍增,用floyd找到最少出现负环的走2^k的最短路,把倍增过程中那些图记下来。倍增floyd就跟矩阵快速幂一样的,因为:把floyd当成一次乘法,走一步的图*走一步的图=走两步的图,走两步的图*走两步的图=走四步的图……
不过有个小问题,走3步出现负环时走四步不一定有,因此初始化时,把邻接矩阵上主对角线都设为0,这样就是“走不超过2^k步时的最短路”,当主对角线出现负数时即这个k是最小的满足走2^k步出现负环的。如果超过log2n即9没出现负环,那这图是没负环的。
接下来就从k-1枚举到0,每个地图和原地图(主对角线0,其他inf)进行一次“乘法”,如果*完不会出现负环就把原地图*=当前地图,这跟lca的倍增差不多。最后答案+1即可。
1 #include<stdio.h> 2 #include<string.h> 3 #include<stdlib.h> 4 #include<algorithm> 5 //#include<iostream> 6 using namespace std; 7 8 int n,m; 9 #define maxn 311 10 typedef int mat[maxn][maxn]; 11 mat mp,f[11],tmp; 12 const int inf=0x3f3f3f3f; 13 void init(mat a) 14 { 15 for (int i=1;i<=n;i++) 16 for (int j=1;j<=n;j++) 17 a[i][j]=inf; 18 } 19 void copy(mat b,mat a) {memcpy(b,a,sizeof(mat));} 20 void mul(mat a,mat b,mat ans) 21 { 22 mat tmp;init(tmp); 23 for (int k=1;k<=n;k++) 24 for (int i=1;i<=n;i++) 25 for (int j=1;j<=n;j++) 26 tmp[i][j]=min(tmp[i][j],a[i][k]+b[k][j]); 27 copy(ans,tmp); 28 } 29 int x,y,v; 30 int main() 31 { 32 scanf("%d%d",&n,&m); 33 init(f[0]); 34 for (int i=1;i<=n;i++) f[0][i][i]=0; 35 for (int i=1;i<=m;i++) 36 { 37 scanf("%d%d%d",&x,&y,&v); 38 f[0][x][y]=v; 39 } 40 int most=1; 41 for (int &i=most;i<=9;i++) 42 { 43 mul(f[i-1],f[i-1],f[i]);bool flag=0; 44 for (int j=1;j<=n;j++) if (f[i][j][j]<0) flag=1; 45 if (flag) break; 46 } 47 if (most>9) puts("0"); 48 else 49 { 50 init(mp); 51 for (int i=1;i<=n;i++) mp[i][i]=0; 52 int ans=0; 53 while (--most>=0) 54 { 55 mul(mp,f[most],tmp);bool flag=0; 56 for (int j=1;j<=n;j++) if (tmp[j][j]<0) flag=1; 57 if (!flag) ans+=1<<most,copy(mp,tmp); 58 } 59 printf("%d\n",ans+1); 60 } 61 return 0; 62 }