BZOJ3504危桥

3504: [Cqoi2014]危桥
Time Limit: 10 Sec Memory Limit: 128 MB
Submit: 946 Solved: 497
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
自己YY了一下建模没想出来,于是参考了一下黄学长的建模方式。。
然而编完发现无端RE无数次。。
感谢Shallwe&DaD3zZ神犇。。
改输出。。
改数组大小。。
终于AC。。
说一下建模思路:
首先源点为0,汇点为60,其实50以上都可以。。
先a1和源点连边,a2和汇点连边,因为一个桥Alice希望往返,容量为2*an。。
Bob同理。。
对于字符矩阵,危桥容量为2,完好的桥容量无穷大。。
但是只做一遍会有问题:比如从a1万一到不了a2,然而最终流到了b2,这样显然是不可行的。那么我们可以把b1和b2反一下再做一遍,如果还是满流就可以了。。
附上本蒟蒻的代码:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
using namespace std;
#define T 60
#define inf 0x7fffffff
int n,f[T][T],q[1000000],h[1000000],dis[1000000],head,tail,cnt=1,ans,sum=0,cur[1000000];
struct node
{
    int to,next,v;
};
node edge[1000000];

void add(int u,int v,int w)
{
    cnt++;
    edge[cnt].next=h[u];
    h[u]=cnt;
    edge[cnt].to=v;
    edge[cnt].v=w;
    cnt++;
    edge[cnt].next=h[v];
    h[v]=cnt;
    edge[cnt].to=u;
    edge[cnt].v=0;
}

bool bfs()
{
    int j,p;
    memset(dis,-1,sizeof(dis));
    head=0;
    tail=1;
    q[0]=0;
    dis[0]=0;
    while (head<tail)
      {
        head++;
        j=q[head];
        p=h[j];
        while (p)
          {
            if (dis[edge[p].to]<0 && edge[p].v>0)
              {
                dis[edge[p].to]=dis[j]+1;
                tail++;
                q[tail]=edge[p].to;
              }
            p=edge[p].next;
          }
      }
    if (dis[T]>0)
      return true;
    else
      return false;
}

int dfs(int x,int f)
{
    int w,used=0,i=h[x];
    if (x==T)
      return f;
    while (i)
      {
        if (edge[i].v && dis[edge[i].to]==dis[x]+1)
          {
            w=f-used;
            w=dfs(edge[i].to,min(w,edge[i].v));
            edge[i].v-=w;
            edge[i^1].v+=w;
            used+=w;
            if (used==f)
              return f;
          }
        i=edge[i].next;
      }
    if (!used)
      dis[x]=-1;
    return used;
}

void build()
{
    int i,j;
    memset(h,0,sizeof(h));
    cnt=1;
    for (i=1;i<=n;i++)
      for (j=1;j<=n;j++)
        {
           if (f[i][j]==1)
             add(i,j,inf);
           if (f[i][j]==2)
             add(i,j,2);
        }
}

int main()
{
    int a1,a2,b1,b2,an,bn,i,j;
    char c[51];
    bool flag;
    while ((scanf("%d%d%d%d%d%d%d",&n,&a1,&a2,&an,&b1,&b2,&bn))!=EOF)
      {
        a1++;
        a2++;
        b1++;
        b2++;
        memset(f,0,sizeof(f));
        flag=false;
        for (i=1;i<=n;i++)
          {
            scanf("%s",&c);
            for (j=1;j<=n;j++)
              {
                if (c[j-1]=='O')
                  f[i][j]=2;
                if (c[j-1]=='N')
                  f[i][j]=1;
              }
          }
        build();
        add(0,a1,2*an);
        add(a2,T,2*an);
        add(0,b1,2*bn);
        add(b2,T,2*bn);
        ans=0;
        while (bfs())
          while (sum=dfs(0,inf))
            ans+=sum;
        if (ans<2*(an+bn))
          flag=true;
        if (!flag)
          {
            build();
            add(0,a1,2*an);
            add(a2,T,2*an);
            add(0,b2,2*bn);
            add(b1,T,2*bn);
            ans=0;
            for (i=0;i<=T;i++)
              cur[i]=h[i];
            while (bfs())
              while (sum=dfs(0,inf))
                ans+=sum;
            if (ans<2*(an+bn))
              flag=true;
          }
        if (!flag)
          printf("Yes\n");
        else
          printf("No\n");
      }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值