ACM-ICPC 2018 沈阳赛区网络预赛-Fantastic Graph-有源汇上下界可行流

这里写图片描述
这里写图片描述

题意:

给出一个二分图,初始各个点度数均为0,每选择一条边,则相应的两点的度数+1,并给出上下界L~R,求能否构成一个网络,使每个点的度数都在L~R之间

思路:

有源汇上下界可行流,先添加一个源点连接所有左侧点,添加一个汇点,使所有右侧点连向汇点,根据上下界网络流做法,还需添加一个附加源点和附加汇点,表示必要弧
具体方法讲解见https://blog.csdn.net/u011008379/article/details/38306477

代码:

#include<cstdio>
#include<cstring>
#include<queue>
#include<cmath>
#include<iostream>
using namespace std;
const int N=1e5+7;
const int inf=0x3f3f3f3f;

struct node
{
    int to,next,cost;
}e[30*N];
int eid,p[N],c[N];

void init()
{
    eid=0;
    memset(p,-1,sizeof(p));
    memset(c,-1,sizeof(c));
}
void insert(int u,int v,int w)
{
    e[eid].to=v;
    e[eid].cost=w;
    e[eid].next=p[u];
    p[u]=eid++;
}
void addedge(int u,int v,int w)
{
    insert(u,v,w);
    insert(v,u,0);
}

int n,m,sp,tp;
int d[N];
bool bfs()                      //构建层次
{
    memset(d,-1,sizeof(d));
    queue<int>Q;
    d[sp]=0;
    Q.push(sp);
    while(!Q.empty())
    {
        int u=Q.front();
        Q.pop();
        for(int i=p[u];i!=-1;i=e[i].next)
        {
            int v=e[i].to;
            if(d[v]==-1&&e[i].cost){
                d[v]=d[u]+1;
                Q.push(v);
                if(v==tp) return true;
            }
        }
    }
    return ~d[tp];
}
int dfs(int u,int b)
{
    if(u==tp) return b;
    int r=0;
    for(int i=c[u];i!=-1;i=e[i].next){
        int v=e[i].to;
        if(e[i].cost&&d[v]==d[u]+1){
            int x=min(e[i].cost,b-r);
            c[u]=i;
            x=dfs(v,x);
            r+=x;
            e[i].cost-=x;
            e[i^1].cost+=x;
            if(r==b) break;
        }
    }
    if(!r)d[u]=-2;
    return r;
}
int dinic(){
    int total=0,tmp;
    while(bfs()){
        memcpy(c,p,sizeof(p));
        while(tmp=dfs(sp,inf))
        total+=tmp;
    }
    return total;
}
int x[N];
int main()
{
    int k,L,R,cs=0;
    while(scanf("%d%d%d",&n,&m,&k)!=EOF){
        init();
        sp=n+m+1,tp=n+m+2;          //附加源,附加汇
        int ss=n+m+3,tt=n+m+4;      //源点,汇点
        scanf("%d%d",&L,&R);
        int u,v;
        for(int i=1;i<=k;i++){
            scanf("%d%d",&u,&v);
            addedge(u,v+n,1);       //构建网络,左边点编号为1~n,右边点编号为n+1~2*n
        }
        for(int i=1;i<=n;i++){
            addedge(ss,i,R-L);     //源点连向所有左侧点,修改流量为R-L
            addedge(sp,i,L);       //附加源连向所有左侧点,流量为L,表示必要弧
            addedge(ss,tp,L);      //源点连向附加汇
        }
        for(int i=1;i<=m;i++){
            addedge(i+n,tt,R-L);    //右侧点连向汇点,修改流量为R-L
            addedge(sp,tt,L);       //附加源连向汇点
            addedge(i+n,tp,L);      //右侧点连向附加汇,流量为L,表示必要弧
        }
        addedge(tt,ss,inf);      //汇点到源点连一条流量为inf的边,变为有源上下界网络流
        printf("Case %d: ",++cs);
        if(dinic()==(n+m)*L) puts("Yes");     //求附加源到附加汇的最大流,若满足附加源到附加汇的所有弧都满流,则有可行流
        else puts("No");
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_43093481/article/details/82593870