YBTOJ危桥通行&洛谷P3163:危桥通行(网络流)

题目描述

Alice 和 Bob 居住在一个由 NN 座岛屿组成的国家,岛屿被编号为 00 到 N-1N−1。某些岛屿之间有桥相连,桥上的道路是双向的,但一次只能供一人通行。其中一些桥由于年久失修成为危桥,最多只能通行两次。

Alice 希望在岛屿 a1和 a2 之间往返 an次(从 a1 到 a2再从 a2 到 a1 算一次往返)。同时,Bob 希望在岛屿 b1和 b2之间往返 bn次。这个过程中,所有危桥最多通行两次,其余的桥可以无限次通行。请问 Alice 和 Bob 能完成他们的愿望吗?

解析

首先可以把往返看成单程,危桥当成只能通过1次
建图方法和显然,但是会忽略a1流向b2、b1流向a2的问题
一个很巧妙的解决办法是交换b1和b2,再跑一次,如果还是满流,就是合法的

代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=2600;
const int M=1e9;
ll read(){
	ll x=0,f=1;char c=getchar();
	while(!isdigit(c)){if(c=='-') f=-1;c=getchar();};
	while(isdigit(c)){x=x*10+c-'0';c=getchar();}
	return x*f;
}
int n,m,s,t;
struct node{
	int to,nxt;ll cap;
}p[N*N*2];
int fi[N],cnt;
void addline(int x,int y,ll cap){
//	printf("x=%d y=%d cap=%lld\n",x,y,cap);
	p[++cnt]=(node){y,fi[x],cap};fi[x]=cnt;
	p[++cnt]=(node){x,fi[y],0};fi[y]=cnt;
}
int bel[N],cur[N];
queue<int>q;
int bfs(){
	memset(bel,0,sizeof(bel));
	bel[s]=1;q.push(s);
	while(!q.empty()){
		int now=q.front();q.pop();
		//printf("now=%d\n",now);
		for(int i=cur[now]=fi[now];~i;i=p[i].nxt){
			int to=p[i].to;
			//printf("  to=%d\n",to);
			if(bel[to]||!p[i].cap) continue;
			//printf("    ok\n");
			bel[to]=bel[now]+1;
			q.push(to);
		}
	}
	return bel[t];
}
ll dfs(int x,ll lim){
	if(x==t||!lim) return lim;
	ll res=0;
	for(int &i=cur[x];~i&&lim;i=p[i].nxt){
		int to=p[i].to;
		if(!p[i].cap||bel[to]!=bel[x]+1) continue;
		ll add=dfs(to,min(lim,p[i].cap));
		res+=add;lim-=add;
		p[i].cap-=add;p[i^1].cap+=add;
		if(!lim) break;
	}
	if(!res) bel[x]=-1;
	return res;
}
ll dinic(){
	ll tot=0;
	while(bfs()){//printf("ok");
		while(ll tmp=dfs(s,2e15)) tot+=tmp;
	}
//	printf("tot=%lld\n",tot);
	return tot;
}
int a1,a2,an,b1,b2,bn;
char ss[60][60];
void build(){
	char c;
	memset(fi,-1,sizeof(fi));cnt=-1;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			c=ss[i][j];
			if(c=='X') continue;
			else if(c=='N') addline(i,j,2e18);
			else addline(i,j,1);
		}
	}
}
int main(){
	while(scanf("%d%d%d%d%d%d%d",&n,&a1,&a2,&an,&b1,&b2,&bn)!=EOF){
		a1++;a2++;b1++;b2++;
		for(int i=1;i<=n;i++){
			scanf(" %s",ss[i]+1);
		}
		s=n+1;t=n+2;
		build();
		addline(s,a1,an);addline(a2,t,an);
		addline(s,b1,bn);addline(b2,t,bn);
		if(dinic()!=an+bn){
			printf("No\n");continue;
		}
		build();
		addline(s,a1,an);addline(a2,t,an);
		addline(s,b2,bn);addline(b1,t,bn);
		if(dinic()!=an+bn) printf("No\n");
		else printf("Yes\n");
	}
    return 0;
}
/*

*/
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值