博客园 首页 私信博主 显示目录 隐藏目录 管理 动画

BZOJ.3504.[CQOI2014]危桥(最大流ISAP)

BZOJ
洛谷

这种题大多是多源多汇跑网络流。往返\(a_n/b_n\)次可以看做去\(a_n/b_n\)次,直接把危桥能走的次数看做\(1\)

先不考虑别的,直接按原图建模:危桥建双向边容量为\(1\),普通桥容量为\(INF\);然后源点\(S\)\(a_1,b_1\)分别连容量\(a_n,b_n\)的边,\(a_2,b_2\)分别向汇点\(T\)连容量\(a_n/b_n\)的边。

这样跑出来的最大流会有两个问题:
一是,\(b_2\to T\)\(b_n\)的一部分流量可能是来自\(a_1\)的,同理\(a_2\to T\)的一些流量可能来自\(b_1\)
二是,危桥只能走一次,但这样可能会正反走两次。

也就是不能直接判断是否满流来判断是否可行。办法是,交换\(b_1,b_2\)\(S\)\(b_2\)\(b_1\)\(T\)),重新建图,再跑最大流。只有两次均满流才一定存在可行方案。

交换\(b_1,b_2\)后再判断是否满流,如果你觉得问题一显然已经被解决了可以跳过下面这段。

如果满流且仍然存在问题一那种情况呢?画个图。
假设第一次跑最大流,\(a_1\to b_2\)的流量为\(x\),那么\(b_1\to b_2\)的流量为\(b_n-x\)\(b_1\to a_2\)的流量也是\(x\)\(a_1\to a_2\)的流量是\(a_n-x\)
而第二次跑最大流,因为是无向图,\(a_1\to a_2\)\(b_2\to b_1\)的流量可以不变,还是\(a_n-x,b_n-x\)。那么\(a_1\to b_1\)\(b_2\to a_2\)的流量也都还是\(x\)
而这两次说明了什么呢,\(a_1\)可以流到\(b_1\) \(x\)流量,还可以流到\(b_2\) \(x\)流量,同时不影响\(a_1\)\(a_2\)\(b_1\)\(b_2\)之间的流量。因为是无向图,将\(a_1\to b_1\)的流量反向,就可以得到\(b_1\to b_2\) \(x\)的流量。\(b_1,b_2\)之间的流就合法了。
同理\(a_1,a_2\)之间的流也合法。
所以如果交换\(b_1,b_2\)后仍满流,一定不存在问题一那种情况。

对于问题二,好多题解都没有写也许是太显然了?

假如\(a_1\to a_2\)正向经过了一座危桥,而\(b_1\to b_2\)反向经过了这座桥,那么交换\(b_1,b_2\),以\(b_2\)为起点后,\(a_1\to a_2,b_2\to b_1\)两条路径都是正向通过了这条边,就受到了流量的限制。
所以如果仍满流,不存在问题二。

所以两遍最大流就可以了。

//924kb	32ms
#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
#define gc() getchar()
typedef long long LL;
const int N=55,M=N*N<<1,INF=0x3f3f3f3f;

int src,des,Enum,H[N],nxt[M],to[M],fr[M],cap[M],pre[N],lev[N];
char s[N][N];

inline int read()
{
	int now=0;register char c=gc();
	for(;!isdigit(c);c=gc());
	for(;isdigit(c);now=now*10+c-'0',c=gc());
	return now;
}
inline void AE(int u,int v,int w)
{
	to[++Enum]=v, fr[Enum]=u, nxt[Enum]=H[u], H[u]=Enum, cap[Enum]=w;
	to[++Enum]=u, fr[Enum]=v, nxt[Enum]=H[v], H[v]=Enum, cap[Enum]=w;
}
inline void AE2(int u,int v,int w)
{
	to[++Enum]=v, fr[Enum]=u, nxt[Enum]=H[u], H[u]=Enum, cap[Enum]=w;
	to[++Enum]=u, fr[Enum]=v, nxt[Enum]=H[v], H[v]=Enum, cap[Enum]=0;
}
bool BFS()
{
	static int q[N];
	for(int i=0; i<des; ++i) lev[i]=des+1;
	int h=0,t=1; q[0]=des, lev[des]=0;
	while(h<t)
	{
		int x=q[h++];
		for(int i=H[x]; i; i=nxt[i])
			if(lev[to[i]]==des+1 && cap[i^1]) lev[to[i]]=lev[x]+1, q[t++]=to[i];
	}
	return lev[0]<=des;
}
inline int Augment()
{
	int mn=INF;
	for(int i=des; i; i=fr[pre[i]])
		mn=std::min(mn,cap[pre[i]]);
	for(int i=des; i; i=fr[pre[i]])
		cap[pre[i]]-=mn, cap[pre[i]^1]+=mn;
	return mn;
}
int ISAP()
{
	static int num[N],cur[N];
	if(!BFS()) return 0;
	memset(num,0,sizeof num);
	for(int i=0; i<=des; ++i) ++num[lev[i]],cur[i]=H[i];
	int res=0,x=0;
	while(lev[0]<=des)
	{
		if(x==des) x=0, res+=Augment();
		bool can=0;
		for(int i=cur[x]; i; i=nxt[i])
			if(lev[to[i]]==lev[x]-1 && cap[i])
			{
				can=1, cur[x]=i, pre[x=to[i]]=i;
				break;
			}
		if(!can)
		{
			int mn=des;
			for(int i=H[x]; i; i=nxt[i])
				if(cap[i]) mn=std::min(mn,lev[to[i]]);
			if(!--num[lev[x]]) break;
			++num[lev[x]=mn+1], cur[x]=H[x];
			if(x) x=fr[pre[x]];
		}
	}
	return res;
}

int main()
{
	int n;
	while(~scanf("%d",&n))
	{
		src=0, des=n+1;
		int a1=read()+1,a2=read()+1,an=read(),b1=read()+1,b2=read()+1,bn=read();
		for(int i=1; i<=n; ++i) scanf("%s",s[i]+1);

		Enum=1, memset(H,0,n+2<<2);
		for(int i=1; i<=n; ++i)
			for(int j=i+1; j<=n; ++j)
				switch(s[i][j])
				{
					case 'O': AE(i,j,1); break;
					case 'N': AE(i,j,INF); break;
				}
		AE2(src,a1,an), AE2(src,b1,bn), AE2(a2,des,an), AE2(b2,des,bn);
		if(ISAP()!=an+bn) {puts("No"); continue;}

		Enum=1, memset(H,0,n+2<<2);
		for(int i=1; i<=n; ++i)
			for(int j=i+1; j<=n; ++j)
				switch(s[i][j])
				{
					case 'O': AE(i,j,1); break;
					case 'N': AE(i,j,INF); break;
				}
		AE2(src,a1,an), AE2(src,b2,bn), AE2(a2,des,an), AE2(b1,des,bn);
		if(ISAP()!=an+bn) {puts("No"); continue;}
		puts("Yes");
	}
	return 0;
}
posted @ 2018-12-30 21:16  SovietPower  阅读(1303)  评论(0编辑  收藏  举报