洛谷 P3163 [CQOI2014]危桥 / YbtOJ「图论」第2章 网络流初探 E. 危桥通行 题解--zhengjun
思路
这题的转化很妙,我看了一下题解才想明白。
首先显然往返不需要走一个环,直接原路返回可以达到最优,所以危桥至多只能走 \(2\) 次可以和往返直接抵消掉。
变成危桥最多只能走 \(1\) 次,不需要往返,只要过去就行了。
那么想到用网络流,从超级源 \(s\) 向 源点 \(a_1,b_1\) 连边,从汇点 \(a_2,b_2\) 向超级汇 \(t\) 连边,流量各为 \(a_n,b_n\)。
然后普通的桥直接建两条边(正向和反向),流量都是 \(+\infty\)。
危桥的话也建两条边(正向和反向),流量都是 \(1\)。
但是这样会有两个问题:
-
可能存在 \(a_1\to b_2\) 和 \(b_1\to a_2\) 的流量;
-
危桥可能被来回走好几次。
先考虑问题二,例如 \(a_1\) 经过 \(u\to v\) 流到 \(a_2\),\(b_1\) 经过 \(v\to u\) 流到 \(b_2\),流量都为 \(x\)。
那么相当于 \(a_1\) 不经过 \(u\to v\) 流到 \(b_2\),\(b_1\) 不经过 \(v\to u\) 流到 \(a_2\),流量都为 \(x\)。
这样问题二就转化为了问题一。
只要解决问题一就行了。
现在这个转化我实在没想出来,看了题解才知道。
考虑交换 \(b_1,b_2\),这样原来从 \(a_1\) 流到 \(b_2\) 的流量变成了源点之间的流量,相当于没有,从 \(b_1\) 到 \(a_2\) 的流量同理。
那么如果流量还是能流满的话,说明 \(a_1\) 到 \(a_2\) 和 \(b_1\) 到 \(b_2\) 都是能流满的,直接跑两次最大流就行了。
代码
#include<bits/stdc++.h>
using namespace std;typedef long long ll;const int N=55,M=N*N*2,inf=1e9;char a[N][N];
int n,s,t,s1,t1,f1,s2,t2,f2,head[N],kk,d[N],cur[N];struct edges{int to,c,nex;}edge[M];
void add(int u,int v,int c1,int c2){edge[++kk]={v,c1,head[u]};head[u]=kk;edge[++kk]={u,c2,head[v]};head[v]=kk;}
void link(int s1,int t1,int f1,int s2,int t2,int f2){
memset(head,0,sizeof head);kk=1;add(s,s1,f1,0);add(s,s2,f2,0);add(t1,t,f1,0);add(t2,t,f2,0);
for(int i=1;i<=n;i++)for(int j=i+1;j<=n;j++)if(a[i][j]=='N')add(i,j,inf,inf);else if(a[i][j]=='O')add(i,j,1,1);
}
bool bfs(){
queue<int>q;q.push(s);memset(d,-1,sizeof d);d[s]=0;cur[s]=head[s];for(int u;!q.empty();q.pop()){
u=q.front();for(int i=head[u],v;v=edge[i].to,i;i=edge[i].nex)
if(!~d[v]&&edge[i].c)d[v]=d[u]+1,cur[v]=head[v],q.push(v);
}return ~d[t];
}
int dfs(int u,int lim=inf){
if(u==t)return lim;int flow=0;for(int i=cur[u],v;v=edge[i].to,i&&flow<lim;i=edge[i].nex){
cur[u]=i;if(d[v]!=d[u]+1||!edge[i].c)continue;int f=dfs(v,min(lim-flow,edge[i].c));
if(!f)d[v]=-1;edge[i].c-=f;edge[i^1].c+=f;flow+=f;
}return flow;
}
int dinic(){int maxflow=0;while(bfs())maxflow+=dfs(s);return maxflow;}
int get(){
s=0;t=n+1;for(int i=1;i<=n;i++)scanf("%s",a[i]+1);link(s1,t1,f1,s2,t2,f2);
if(dinic()!=f1+f2)return 0;link(s1,t1,f1,t2,s2,f2);return dinic()==f1+f2;
}
int main(){
for(;~scanf("%d%d%d%d%d%d%d",&n,&s1,&t1,&f1,&s2,&t2,&f2);)s1++,s2++,t1++,t2++,puts(get()?"Yes":"No");return 0;
}