BZOJ.3504.[CQOI2014]危橋(最大流ISAP)
這種題大多是多源多匯跑網路流。往返\(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_2\)和\(b_2\to a_1\)的流量也都還是\(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;
}