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
",ans+1);
60     }
61     return 0;
62 }
View Code
原文地址:https://www.cnblogs.com/Blue233333/p/7636520.html