【BZOJ3504】【CQOI2014】—危桥(最大流建模)

传送门

不错的一道网络流连手题

一般第一眼都会直接想到的一种似乎正确的做法:

对于危桥连一个流量为 1 1 1的边,普通桥连一条 i n f inf inf的边
源点向 a l , b l al,bl al,bl a n , b n an,bn an,bn的边, a 2 , b 2 a2,b2 a2,b2向汇点连一条为 a n , b n an,bn an,bn的边
看是不是满流

然而这样是错的…

比如这样

在这里插入图片描述

很明显是错的

那么怎么办?

我们考虑交换其中一对源汇点
然后再跑一次,看是不是仍然是最大流?

为什么是对的呢?

假设我们交换的是 b l , b 2 bl,b2 bl,b2
如果流满了
考虑两种情况:


1. a l al al b 2 b2 b2流满的

由于第一次的时候 a l al al b l bl bl可以流满
那也就是说 b l bl bl b 2 b2 b2也可以流满了
又第一次 b l bl bl,第二次 b 2 b2 b2都和 a 2 a2 a2能流满
a l al al也总可以流给 a 2 a2 a2
那也就说是可以的


2. a l al al a 2 a2 a2流满的

那不用说了,既然 a l al al可以流满 a 2 a2 a2,那肯定就是可以了的


所以如果两次都流满了,那肯定就是可以的
反之肯定不行

#include<bits/stdc++.h>
using namespace std;
inline int read(){
	char ch=getchar();
	int res=0,f=1;
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch))res=(res<<3)+(res<<1)+(ch^48),ch=getchar();
	return res*f;
}
const int N=71;
const int M=5005;
const int inf=20030224;
int adj[N],nxt[M],to[M],cap[M],tp[N],lev[N],n,cnt,str,des,as,at,an,bs,bt,bn;
char edg[N][N];
inline void addedge(int u,int v,int w){
	nxt[++cnt]=adj[u],adj[u]=cnt,to[cnt]=v,cap[cnt]=w;
	nxt[++cnt]=adj[v],adj[v]=cnt,to[cnt]=u,cap[cnt]=0;
}
inline bool bfs(){
	queue<int> q;
	memset(lev,-1,sizeof(lev));
	q.push(str),lev[str]=0;
	while(!q.empty()){
		int u=q.front();q.pop();
		for(int e=adj[u];e;e=nxt[e]){
			int v=to[e];
			if(cap[e]>0&&lev[v]==-1){
				lev[v]=lev[u]+1,q.push(v);
				if(v==des)return true;
			}
		}
	}
	return false;
}
inline int dinic(int u,int flow){
	if(u==des)return flow;
	int res=0;
	for(int &e=tp[u];e;e=nxt[e]){
		int v=to[e];
		if(lev[v]==lev[u]+1&&cap[e]>0){
			int mn=dinic(v,min(flow-res,cap[e]));
			cap[e]-=mn,cap[e^1]+=mn,res+=mn;
			if(flow==res)return res;
		}
	}
	return res;
}
inline int solve(){
	int res=0;
	while(bfs()){
		memcpy(tp,adj,sizeof(adj));
		res+=dinic(str,inf);
	}
	return res;
}
inline void buildedge(){
	memset(adj,0,sizeof(adj)),memset(cap,0,sizeof(cap)),cnt=1;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			switch(edg[i][j]){
				case 'O':{
					addedge(i,j,1);
					break;
				}
				case 'N':{
					addedge(i,j,inf);
					break;
				}
				case 'X':{
					break;
				}
			}
		}
	}
}
int main(){
	while(scanf("%d",&n)!=EOF){
		as=read()+1,at=read()+1,an=read(),bs=read()+1,bt=read()+1,bn=read();
		str=n+1,des=n+2;
		for(int i=1;i<=n;i++)scanf("%s",edg[i]+1);
		buildedge();
		addedge(str,as,an),addedge(str,bs,bn),addedge(at,des,an),addedge(bt,des,bn);
		int fres=solve();
		buildedge();
		addedge(str,as,an),addedge(str,bt,bn),addedge(at,des,an),addedge(bs,des,bn);
		int res=solve();
		if(res==an+bn&&fres==an+bn)cout<<"Yes"<<'\n';
		else cout<<"No"<<'\n';
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值