【CQOI2014】危桥

【CQOI2014】危桥

Description

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

Input

本题有多组测试数据。
每组数据第一行包含7个空格隔开的整数,分别是\(N,a1,a2,an,b1,b2,bn\)
接下来是一个N行N列的对称矩阵,由大写字母组成。矩阵的第i行第j列描述编号i-1和j-1的岛屿间连接情况,若为“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

网络流。

首先因为是走来回,所以我们就每找到一条路径就原路返回。所以危桥的容量变成1。

然后我们建\((S,a1,an),(S,b1,bn)\)以及\((a2,T,an),(b2,T,bn)\)。其他的边照常建。

如果满流就能证明可行吗?

反例就是\(a1\)流向\(b2\)\(b1\)流向\(a2\)

于是我们交换\(b1,b2\),再跑一边网络流,仍然满流就成立。

交换过后万一还是出现了上述的反例呢?我们发现,如果出现这种情况,那么一定有足够的\(a1\)\(a2\)\(b1\)\(b2\)的流量。

代码:

#include<bits/stdc++.h>
#define ll long long
#define N 55

using namespace std;
inline int Get() {int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}while('0'<=ch&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}return x*f;}

int n,a[N][N];
int a1,a2,b1,b2;
int an,bn;
struct load {
    int to,next;
    int flow;
}s[N*N<<1];
int h[N],cnt;
void add(int i,int j,int f) {
    s[++cnt]=(load) {j,h[i],f};h[i]=cnt;
    s[++cnt]=(load) {i,h[j],0};h[j]=cnt;
}
void Init() {
    cnt=1;
    memset(h,0,sizeof(h));
}
int S,T;
int dis[N],gap[N];
int dfs(int v,int maxf) {
    if(v==T) return maxf;
    int ret=0;
    for(int i=h[v];i;i=s[i].next) {
        int to=s[i].to;
        if(s[i].flow&&dis[to]+1==dis[v]) {
            int dlt=dfs(to,min(maxf,s[i].flow));
            s[i].flow-=dlt;
            s[i^1].flow+=dlt;
            ret+=dlt;
            maxf-=dlt;
            if(!maxf||dis[S]==n+2) return ret;
        }
    }
    if(!(--gap[dis[v]])) dis[S]=n+2;
    gap[++dis[v]]++;
    return ret;
}

bool sap(int a1,int a2,int an,int b1,int b2,int bn) {
    Init();
    add(S,a1,an),add(S,b1,bn);
    add(a2,T,an),add(b2,T,bn);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            if(a[i][j]==1) add(i,j,1);
            else if(a[i][j]==2) add(i,j,1e9);
    memset(dis,0,sizeof(dis));
    memset(gap,0,sizeof(gap));
    gap[0]=n+2;
    int ans=0;
    while(dis[S]<=n+2) ans+=dfs(S,1e9);
    return ans==an+bn;
}

int main() {
    while(scanf("%d",&n)!=EOF) {
        a1=Get()+1,a2=Get()+1,an=Get();
        b1=Get()+1,b2=Get()+1,bn=Get();
        char x;
        S=0,T=n+1;
        for(int i=1;i<=n;i++) {
            for(int j=1;j<=n;j++) {
                while(x=getchar(),x!='X'&&x!='O'&&x!='N');
                if(x=='O') a[i][j]=1;
                else if(x=='N') a[i][j]=2;
                else a[i][j]=0;
            }
        }
        int flag=0;
        if(sap(a1,a2,an,b1,b2,bn)) {
            swap(b1,b2);
            flag=sap(a1,a2,an,b1,b2,bn);
        }
        (flag)?cout<<"Yes\n":cout<<"No\n";
    }
    return 0;
}

转载于:https://www.cnblogs.com/hchhch233/p/10367220.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值