BZOJ 3504 [Cqoi2014] 危桥 最大流

题意:链接

方法:最大流

解析:这道题其实第一眼看上去是(和谐)题,然后发现水水的写完后WA了,然后从网上下来数据来观测数据,发现自己WA的地方都是同一个地方,之后上网上看了看其他人的姿势,算是懂了这道题吧。

首先读完题后,第一眼被这个往返卡了一会,后来发现,假如说从A到B,AB间是危桥的话,那你顶多在其中往返一次,也就是对应流量为1,如此的话,因为无向,所以B到A也必然有流量为1的一条路,在加上点细节修饰以及自己手画个图就能明白。

接下来我就以为这道题已经完事了,直接写了发最大流一跑发现WA了!

后经我亲测数据发现有的时候的流量并非自己所希望的从a1流向a2,有的满流的情况居然是从a1流到b2,这样怎么搞?

然后题解里给出了种办法,就是跑完第一次最大流后如果是满流则重新建图,其中桥的部分不变,源点到a1,a2到汇点的部分不变,只需要把原来的源点到b1,b2到汇点改为源点到b2,b1到汇点即可,再次判断,如果这样跑出来还是满流的话,就有解。

不过遗憾的是我并没有找到证明,所以就自己写了个证明,不知其严谨性。

第一次满流后

假设a1->a2 流量为 an-x

a1->b2 流量为 x

b1->a2 流量为 x

b1->b2 流量为 bn-x

之后我们假设第二次跑完之后又达到满流。

则a1->a2 流量仍为 an-x

∴a1->b1 流量为 x

b2->a2 流量为 x

b2->b1 流量为bn-x

又先前a1->b2 流量为x

∴a1->b2->b1流量为x(无向图)

则b2->b1又可有流量为x的一条路

又b2->b1已有流量为bn-x的一条路

∴b2->b1有流量为bn的一条路

则a1->a2有流量为an的一条路

得证

于是乎按照这个思路再跑一遍最大流就行了。

代码:

优于观测数据以及其他不可抗力原因,我的代码及其丑陋,所以他人的正常代码和我的解题思路更配哦- -

#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 2700
#define INF 0x3f3f3f3f
using namespace std;
struct node 
{
    int to,val,next;
}edge[N*N];
int head[N],dep[N],map[N][N];
int n,a1,a2,an,b1,b2,bn,cnt;
char s[N];
void init()
{
    memset(head,-1,sizeof(head));cnt=0;
}
void edgeadd(int from,int to,int val)
{
    edge[cnt].to=to;
    edge[cnt].val=val;
    edge[cnt].next=head[from];
    head[from]=cnt++;
}
int bfs(int s,int e)
{
    memset(dep,0,sizeof(dep));
    queue<int>q;
    q.push(s);
    dep[s]=1;
    while(!q.empty())
    {
        int u=q.front();
        if(u==e)return 1;
        q.pop();
        for(int i=head[u];i!=-1;i=edge[i].next)
        {
            int to=edge[i].to;
            if(edge[i].val==0||dep[to]!=0)continue;
            dep[to]=dep[u]+1;
            q.push(to);
        }
    }
    return 0;
}
int dfs(int s,int max_vale)
{
    int ret=0,tmp;
    if(s==n*n+1)return max_vale;
    for(int i=head[s];i!=-1;i=edge[i].next)
    {
        int to=edge[i].to;
        if(edge[i].val==0||dep[to]!=dep[s]+1)continue;
        tmp=dfs(to,min(max_vale-ret,edge[i].val));
        edge[i].val-=tmp;
        edge[i^1].val+=tmp;
        ret+=tmp;
        if(ret==max_vale)return ret;
    }
    return ret;
}
void build()
{
    init();
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            if(map[i][j]==1)
            {
                edgeadd(i,j,INF);
                edgeadd(j,i,0);
            }else if(map[i][j]==2)
            {
                edgeadd(i,j,1);
                edgeadd(j,i,0);
            }
        }
    }
}
int main()
{
//  freopen("bridge.in","r",stdin);
//  freopen("bridge.out","w",stdout);
    while(~scanf("%d%d%d%d%d%d%d",&n,&a1,&a2,&an,&b1,&b2,&bn))
    {
        memset(map,0,sizeof(map));
        init();
        a1++,a2++,b1++,b2++;
        for(int i=1;i<=n;i++)
        {
            scanf("%s",s);
            for(int j=0;j<n;j++)
            {
                if(s[j]=='N')
                {
                    map[i][j+1]=1;
                }else if(s[j]=='O')
                {
                    map[i][j+1]=2;
                }
            }
        }
        build();
        edgeadd(0,a1,1*an);
        edgeadd(a1,0,0);
        edgeadd(a2,n*n+1,1*an);
        edgeadd(n*n+1,a2,0);
        edgeadd(0,b1,1*bn);
        edgeadd(b1,0,0);
        edgeadd(b2,n*n+1,1*bn);
        edgeadd(n*n+1,b2,0); 
        int ret=0,flag=0;
        while(bfs(0,n*n+1))
        {
            while(int t=dfs(0,INF))
            {
                ret+=t;
            }
        }
        if(ret<1*(an+bn)){flag=1;}
        if(!flag)
        {
            build();
            edgeadd(0,a1,1*an);
            edgeadd(a1,0,0);
            edgeadd(a2,n*n+1,1*an);
            edgeadd(n*n+1,a2,0);
            edgeadd(0,b2,1*bn);
            edgeadd(b2,0,0);
            edgeadd(b1,n*n+1,1*bn);
            edgeadd(n*n+1,b1,0); 
            ret=0;
            while(bfs(0,n*n+1))
            {
                while(int t=dfs(0,INF))
                {
                    ret+=t;
                }
            }
            if(ret<1*(an+bn)){flag=1;}
        }
        if(flag)printf("No\n");
        else printf("Yes\n");
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值