【BZOJ4773】负环 [SPFA][二分]
负环
Time Limit: 100 Sec Memory Limit: 256 MB[Submit][Status][Discuss]
Description
在忘记考虑负环之后,黎瑟的算法又出错了。对于边带权的有向图 G = (V, E),请找出一个点数最小的环,使得环上的边权和为负数。保证图中不包含重边和自环。
Input
第1两个整数n, m,表示图的点数和边数。
接下来的m行,每<=三个整数ui, vi, wi,表<=有一条从ui到vi,权值为wi的有向边。
Output
仅一行一个整数,表示点数最小的环上的点数,若图中不存在负环输出0。
Sample Input
3 6
1 2 -2
2 1 1
2 3 -10
3 2 10
3 1 -10
1 3 10
1 2 -2
2 1 1
2 3 -10
3 2 10
3 1 -10
1 3 10
Sample Output
2
HINT
2 <= n <= 300
0 <= m <= n^2
1 <= ui, vi <= n
|wi| <= 10^4
Main idea
给定若干单向边,找出点数最小的负环。
Solution
显然直接二分答案,用DfsSPFA限制深搜层数判断是否存在可行负环即可。
Code
1 #include<iostream>
2 #include<algorithm>
3 #include<cstdio>
4 #include<cstring>
5 #include<cstdlib>
6 #include<cmath>
7 using namespace std;
8
9 const int ONE = 305;
10 const int EDG = ONE*ONE;
11
12 int n,m;
13 int x,y,z;
14 int next[EDG],first[EDG],go[EDG],w[EDG],tot;
15 int vis[ONE],dist[ONE];
16 int PD;
17
18 int get()
19 {
20 int res=1,Q=1; char c;
21 while( (c=getchar())<48 || c>57)
22 if(c=='-')Q=-1;
23 if(Q) res=c-48;
24 while((c=getchar())>=48 && c<=57)
25 res=res*10+c-48;
26 return res*Q;
27 }
28
29 void Add(int u,int v,int z)
30 {
31 next[++tot]=first[u]; first[u]=tot; go[tot]=v; w[tot]=z;
32 }
33
34 void Spfa(int u,int T,int Limit)
35 {
36 if(PD) return;
37 for(int e=first[u];e;e=next[e])
38 {
39 int v = go[e];
40 if(dist[u]+w[e] <= dist[v])
41 {
42 if(vis[v]) {PD = 1; return;}
43 if(T==Limit) return;
44 dist[v] = dist[u] + w[e];
45 vis[v] = 1;
46 Spfa(v,T+1,Limit);
47 vis[v] = 0;
48 }
49 }
50 }
51
52 int Check(int Limit)
53 {
54 PD = 0;
55 for(int i=1;i<=n;i++)
56 {
57 memset(vis,0,sizeof(vis)); vis[i] = 1;
58 memset(dist,0,sizeof(dist));
59 Spfa(i,1,Limit);
60 if(PD) return 1;
61 }
62 return 0;
63 }
64
65 int main()
66 {
67 n=get(); m=get();
68 for(int i=1;i<=m;i++)
69 {
70 x=get(); y=get(); z=get();
71 Add(x,y,z);
72 }
73
74 if(!Check(n)) {printf("0"); exit(0);}
75
76 int l=1, r=n;
77 while(l < r-1)
78 {
79 int mid = l+r>>1;
80 if(Check(mid)) r = mid;
81 else l = mid;
82 }
83
84 if(Check(l)) printf("%d",l);
85 else printf("%d",r);
86 }
87