负环 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 }