【BZOJ4773】负环 [SPFA][二分]

时间:2021-08-01 12:53:33

负环

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

 #include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
using namespace std; const int ONE = ;
const int EDG = ONE*ONE; int n,m;
int x,y,z;
int next[EDG],first[EDG],go[EDG],w[EDG],tot;
int vis[ONE],dist[ONE];
int PD; int get()
{
int res=,Q=; char c;
while( (c=getchar())< || c>)
if(c=='-')Q=-;
if(Q) res=c-;
while((c=getchar())>= && c<=)
res=res*+c-;
return res*Q;
} void Add(int u,int v,int z)
{
next[++tot]=first[u]; first[u]=tot; go[tot]=v; w[tot]=z;
} void Spfa(int u,int T,int Limit)
{
if(PD) return;
for(int e=first[u];e;e=next[e])
{
int v = go[e];
if(dist[u]+w[e] <= dist[v])
{
if(vis[v]) {PD = ; return;}
if(T==Limit) return;
dist[v] = dist[u] + w[e];
vis[v] = ;
Spfa(v,T+,Limit);
vis[v] = ;
}
}
} int Check(int Limit)
{
PD = ;
for(int i=;i<=n;i++)
{
memset(vis,,sizeof(vis)); vis[i] = ;
memset(dist,,sizeof(dist));
Spfa(i,,Limit);
if(PD) return ;
}
return ;
} int main()
{
n=get(); m=get();
for(int i=;i<=m;i++)
{
x=get(); y=get(); z=get();
Add(x,y,z);
} if(!Check(n)) {printf(""); exit();} int l=, r=n;
while(l < r-)
{
int mid = l+r>>;
if(Check(mid)) r = mid;
else l = mid;
} if(Check(l)) printf("%d",l);
else printf("%d",r);
}