负环 BZOJ 4773

负环

【问题描述】

在忘记考虑负环之后,黎瑟的算法又出错了。对于边带权的有向图 G = (V, E),请找出一个点数最小的环,使得环上的边权和为负数。保证图中不包含重边和自环。

【输入格式】

第1两个整数n, m,表示图的点数和边数。
接下来的m行,每<=三个整数ui, vi, wi,表<=有一条从ui到vi,权值为wi的有向边。

【输出格式】

仅一行一个整数,表示点数最小的环上的点数,若图中不存在负环输出0。

【样例输入】

3 6
1 2 -2
2 1 1
2 3 -10
3 2 10
3 1 -10
1 3 10

【样例输出】

2

【数据范围】

2 <= n <= 300, 0 <= m <= n(n <= 1), 1 <= ui, vi <= n, |wi| <= 10^4

题解:

考虑二分最小点数,用 Floyed (用倍增来限制步数)检验答案的范围区间

那么将二分过程化为倍增的过程,可行时不断缩小答案,否则继承当前答案

 1 #include<cmath>
 2 #include<cstdio>
 3 #include<cstdlib>
 4 #include<cstring>
 5 #include<iostream>
 6 #include<algorithm>
 7 using namespace std;
 8 const int maxn = 301;
 9 const int logn = 6;
10 inline int Get()
11 {
12     int x;
13     char c;
14     bool o = false;
15     while((c = getchar()) < '0' || c > '9')
16         if(c == '-') o = true;
17     x = c - '0';
18     while((c = getchar()) >= '0' && c <= '9')
19         x = x * 10 + c - '0';
20     return (o) ? -x : x;
21 }
22 int n, m;
23 int f[maxn][maxn][logn + 1], g[maxn][maxn], s[maxn][maxn];
24 inline void Clear()
25 {
26     memset(f, 127 / 2, sizeof(f));
27     for(int k = 0; k <= logn; ++k)
28         for(int i = 1; i <= n; ++i)
29             f[i][i][k] = 0;
30     memset(s, 127 / 2, sizeof(s));
31     for(int i = 1; i <= n; ++i) s[i][i] = 0;
32 }
33 int main()
34 {
35     n = Get(), m = Get();
36     Clear();
37     for(int i = 1; i <= m; ++i)
38     {
39         int x = Get(), y = Get(), z = Get();
40         f[x][y][0] = z;
41     }
42     for(int l = 1; l <= logn; ++l)
43         for(int i = 1; i <= n; ++i)
44             for(int j = 1; j <= n; ++j)
45                 for(int k = 1; k <= n; ++k)
46                     f[i][j][l] = min(f[i][j][l], f[i][k][l - 1] + f[k][j][l - 1]);
47     int ans = 1;
48     for(int l = logn; l >= 0; --l)
49     {
50         memcpy(g, s, sizeof(g));
51         for(int i = 1; i <= n; ++i)
52             for(int j = 1; j <= n; ++j)
53                 for(int k = 1; k <= n; ++k)
54                     s[i][j] = min(s[i][j], g[i][k] + f[k][j][l]);
55         bool flag = false;
56         for(int i = 1; i <= n; ++i)
57             if(s[i][i] < 0)
58             {
59                 flag = true;
60                 break;
61             }
62         if(flag) memcpy(s, g, sizeof(s));
63         else ans += (1 << l);
64     }
65     if(ans > n) printf("0\n");
66     else printf("%d\n", ans);
67 }
posted @ 2017-03-25 11:40  草根柴鸡  阅读(276)  评论(0编辑  收藏