【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

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 
View Code
posted @ 2017-03-26 22:04  BearChild  阅读(660)  评论(0编辑  收藏  举报