zoukankan      html  css  js  c++  java
  • 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 o T)(b_n)的一部分流量可能是来自(a_1)的,同理(a_2 o T)的一些流量可能来自(b_1)
    二是,危桥只能走一次,但这样可能会正反走两次。

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

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

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

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

    假如(a_1 o a_2)正向经过了一座危桥,而(b_1 o b_2)反向经过了这座桥,那么交换(b_1,b_2),以(b_2)为起点后,(a_1 o a_2,b_2 o 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;
    }
    
  • 相关阅读:
    第一周C语言作业
    C语言I博客园作业08
    C语言I博客作业07
    C语言I博客作业06
    C语言I博客作业05
    C语言I博客作业04
    C语言II博客作业04
    C语言II博客作业03
    C语言II博客作业02
    C语言II博客作业01
  • 原文地址:https://www.cnblogs.com/SovietPower/p/10200725.html
Copyright © 2011-2022 走看看