BZOJ4773: 负环[二分]

题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=4773

233可能我是因为没看懂倍增的做法才去学的二分做法的,本题我们是要求最小的答案,答案是客观已经存在的(不像dp,你需要做一些选择),显然是满足单调性的,因此我们可以二分,再利用DFS-SPFA找负环的方法,我们限制SPFA搜索的层数,不就是限制点数的范围了吗?
(数据小我猜你也卡不了)

AC Code:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#define rg register
#define il inline
#define maxn 505
#define ll long long
#define eps 1e-8
#define lid id << 1
#define rid (id << 1) | 1
#define rep(a,b,c) for (rg int a = 1 ; a <= c ; a += b)
using namespace std;
il int read(){rg int x = 0 , w = 1;rg char ch = getchar();while (ch < '0' || ch > '9'){if (ch == '-') w = -1;ch = getchar();}while (ch >= '0' && ch <= '9'){x = (x << 3) + (x << 1) + ch - '0';ch = getchar();}return x * w;}
int head[500005] ,  cnt , n , m , num[maxn] , dis[maxn];
int vis[500005] , vis0;
bool ok = 0;
struct edge{
    int fr , to , next , v;
}e[500005];
void add(int u,int v,int w){
    e[++cnt].to = v;
    e[cnt].next = head[u];
    head[u] = cnt;
    e[cnt].v = w;
}
void spfa(int now , int depth , int lmt){
	if (ok) return;
	for (rg int i = head[now] ; i ; i = e[i].next){
		rg int to = e[i].to;
		if (dis[to] >= dis[now] + e[i].v){
			if (vis[to] == vis0) {ok = 1 ; return;}
			if (depth == lmt) return;
			dis[to] = dis[now] + e[i].v;
			vis[to] = vis0;
			spfa(to , depth + 1 , lmt);
			vis[to]--;
		}
	}
}
bool check(int mid){
    for (rg int i = 1 ; i <= n ; ++i){
    	ok = 0;
    	++vis0;
    	memset(dis , 0 , sizeof(dis)); // be careful!
    	vis[i] = vis0;
    	spfa(i , 1 , mid);
		if (ok) return 1;	
	}
	return 0;
}
int main(){
    n = read() , m = read();
	rg int u , v , w;
   	for (rg int i = 1 ; i <= m ; ++i){
 	   	u = read() , v = read() , w = read();
    	add(u , v , w);
   		//if (w >= 0 ) add(v , u , (double)w);
   	}
   	if (!check(n)) {cout << "0";return 0;}
	rg int l = 1 , r = n , ans = 9999999;
	while (l <= r){
		int mid = (l + r) >> 1;
		if (check(mid)) ans = mid , r = mid - 1;
		else l = mid + 1;
	}
	cout << ans;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值