【bzoj3504】[Cqoi2014]危桥 网络流

Description

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

Input

本题有多组测试数据。
每组数据第一行包含7个空格隔开的整数,分别为N、al、a2、an、bl、b2、bn。
接下来是一个N行N列的对称矩阵,由大写字母组成。矩阵的i行j列描述编号i一1和j-l的岛屿间的连接情况,若为“O”则表示有危桥相连:为“N”表示有普通的桥相连:为“X”表示没有桥相连。
|

Output

对于每组测试数据输出一行,如果他们都能完成愿望输出“Yes”,否则输出“No”。

Sample Input

4 0 1 1 2 3 1

XOXX

OXOX

XOXO

XXOX

4 0 2 1 1 3 2

XNXO

NXOX

XOXO

OXOX

Sample Output

Yes

No

数据范围

4<=N<50

O<=a1, a2, b1, b2<=N-1

1 <=an. b<=50

题解
转自hzwer
网络流即可。。。但是只做一遍会有问题:比如从a1万一到不了a2,然而最终流到了b2,这样显然是不可行的。那么我们可以把b1和b2反一下再做一遍,如果还是满流就可以了。

代码

#include<bits/stdc++.h>
#define ll long long
#define inf 1000000000
#define mod 1000000007
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
bool flag;
int n,a1,a2,an,b1,b2,bn;
int tot,ans;
int h[55],q[55];
int mp[55][55],S,T;
int Head[55],ret[100006],len[100005],Next[100005],dis[55],gap[55];
inline void ins(int u,int v,int l)
{
    ret[++tot]=v;len[tot]=l;
    Next[tot]=Head[u];Head[u]=tot;
}
inline void insert(int u,int v,int l)
{
    ins(u,v,l);ins(v,u,0);
}
void build()
{
    memset(dis,0,sizeof(dis));
    memset(gap,0,sizeof(gap));
    memset(Head,0,sizeof(Head));tot=1;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            if(mp[i][j]==1)insert(i,j,2);
            else if(mp[i][j]==2)insert(i,j,inf);
}
int isap(int u,int aug)
{
    if (u==T||aug==0) return aug;
    int flow=0,min_d=T;
    for (int i=Head[u];i;i=Next[i])
    {
        int v=ret[i];
        if (len[i]>0)
        {
            if (dis[u]==dis[v]+1)
            {
                int t=isap(ret[i],min(aug-flow,len[i]));
                len[i]-=t;len[i^1]+=t;flow+=t;
            }
            if (flow==aug) return flow;
            if (dis[S]==T) return flow;
            min_d=min(min_d,dis[v]+1);
        }
    }
    if (flow==0)
    {
        gap[dis[u]]--;
        if (gap[dis[u]]==0) dis[S]=T;
        dis[u]=min_d;
        gap[dis[u]]++;
    }
    return flow;
}
int main()
{
    while(scanf("%d%d%d%d%d%d%d",&n,&a1,&a2,&an,&b1,&b2,&bn)!=EOF)
    {
        memset(mp,0,sizeof(mp));flag=0;
        a1++;a2++;b1++;b2++;S=n+1;T=n+2;
        for(int i=1;i<=n;i++)
        {
            char ch[55];
            scanf("%s",ch);
            for(int j=1;j<=n;j++)
                if(ch[j-1]=='O')mp[i][j]=1;
                else if(ch[j-1]=='N')mp[i][j]=2;
        }
        build();
        insert(S,a1,an*2);insert(a2,T,an*2);
        insert(S,b1,bn*2);insert(b2,T,bn*2);
        ans=0;
        while (dis[S]!=T){ans+=isap(S,inf);}
        if(ans<2*(an+bn))flag=1;
        if(!flag)
        {
            build();
            insert(S,a1,an*2);insert(a2,T,an*2);
            insert(S,b2,bn*2);insert(b1,T,bn*2);
            ans=0;
            while (dis[S]!=T){ans+=isap(S,inf);}
            if(ans<2*(an+bn))flag=1;
        }
        if(flag)printf("No\n");
        else printf("Yes\n");
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值