jzoj3597 【CQOI2014】危桥

Alice和Bob居住在一个由N座岛屿组成的国家,岛屿被编号为0到N-1。某些岛屿之间有桥相连,桥上的道路是双向的,但一次只能供一人通行。其中一些桥由于年久失修成为危桥,最多只能通行两次。
Alice希望在岛屿a1和a2之间往返an次(从a1到a2再从a2到a1算一次往返)。同时,Bob希望在岛屿b1和b2之间往返bn次。这个过程中,所有危桥最多通行两次,其余的桥可以无限次通行。请问Alice和Bob能完成他们的愿望吗?

4<=N<=50
0<=a1,a2,b1,b2<=N-1
1<=an,bn<=50

n很小,考虑最大流,由于是无向图,所以一次来回只要找两条路径就可以了。
于是乎判流量是否够2(an+bn).
但会有一种问题,a1的跑到b2去了,a2的跑到b1去了.

在这种情况下,发现只需要将b的起点终点调换,这样再跑一次最大流。
因为无向边,原来a1-a2,b1-b2的肯定是全流完的,假设这两次a1-a2贡献了x,那么就有an-x流到另一边去了。

假如也可以的话,就说明有a1-b1贡献的an-x。也就是原图中可以从a1走到b1 (后一次流说明),a1也可以走到b2(前一次流说明)
两边合并一下加回到b1-b2这条路径上,对于a也是同理。

#include <iostream>
#include <cstring>
#include <cstdio>
#define min(a,b) ((a)<(b)?(a):(b))
using namespace std;
const int N = 55,inf = 1<<29;
int a1,a2,b1,b2,an,bn,n,S,T;
int final[N],to[N*N],nex[N*N],f[N*N],tot;
int gap[N],la[N];

char c[N][N];
void link(int x,int y,int fg) {to[++tot]=y,nex[tot]=final[x],final[x]=tot,f[tot]=fg;}
int go(int x,int fl) {
    if (x==T) return fl;
    int used=0;
    for (int i=final[x]; i; i=nex[i]) if (f[i] && la[to[i]]+1==la[x]) {
        int suc=go(to[i],min(fl-used,f[i]));
        f[i]-=suc,f[i^1]+=suc;
        used+=suc;
        if (used==fl) return fl;
    }
    if (--gap[la[x]++]==0) la[0]=inf;
    else ++gap[la[x]];
    return used;
}
int maxflow() {
    int ans=0; memset(gap,0,sizeof gap),memset(la,0,sizeof la);
    gap[0]=n*n+2;
    while (la[0]!=inf)
        ans+=go(S,inf);
    return ans; 
}
int main() {
    freopen("2.in","r",stdin);
    while (scanf("%d %d %d %d %d %d %d",&n,&a1,&a2,&an,&b1,&b2,&bn)!=EOF) {
        tot=1; memset(final,0,sizeof final);
        a1++,a2++,b1++,b2++;
        for (int i=1; i<=n; i++) {
            scanf("\n"); for (int j=1; j<=n; j++) {
                c[i][j]=getchar();
                if (j<=i) {
                    if (c[i][j]=='O') link(i,j,2),link(j,i,2);
                    else if (c[i][j]=='N') link(i,j,inf),link(j,i,inf);
                }
            }
        }

        S=n+1,T=S+1;
        link(S,a1,2*an),link(a1,S,0);
        link(a2,T,2*an),link(T,a2,0);
        link(S,b1,2*bn),link(b1,S,0);
        link(b2,T,2*bn),link(T,b2,0);
//      cout<<maxflow()<<endl;
        if (maxflow()!=2*(an+bn)) {printf("No\n");continue;}


        tot=1; memset(final,0,sizeof final);
        for (int i=1; i<=n; i++) for (int j=1; j<=i; j++) {
                if (c[i][j]=='O') link(i,j,2),link(j,i,2);
                else if (c[i][j]=='N') link(i,j,inf),link(j,i,inf);
            }
        link(S,a1,2*an),link(a1,S,0);
        link(a2,T,2*an),link(T,a2,0);
        link(S,b2,2*bn),link(b2,S,0);
        link(b1,T,2*bn),link(T,b1,0);
        //cout<<maxflow()<<endl;
        if (maxflow()!=2*(an+bn)) {printf("No\n");continue;}
        printf("Yes\n");
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值