1. 程式人生 > >ACM-ICPC 2018 瀋陽賽區網路預賽

ACM-ICPC 2018 瀋陽賽區網路預賽

題意:

       給出一張二分圖,初始每個節點的度數都為零。選擇若干條邊,使得每個節點的度數範圍再[L,R]範圍內。每選一條邊,邊上兩端的節點度數+1。

做法:

      這道題就是:建一個虛擬源點和一個虛擬匯點,虛擬源點到左邊每個點連一條流量為R-L的邊,右邊每個點到虛擬匯點連一條流量為R-L的邊。中間二分圖的每條邊流量當然為1,由匯點源點連一條流量無限大的邊。之後再建一個超級源點和一個超級匯點,由超級源點向左邊每個點連一條流量為L的邊,由右邊每個點向超級匯點連一條流量為L的邊。跑一遍最大流,如果超級源點和超級匯點所連的邊跑滿(因為一定相等),則存在可行解,否則不存在。

#include<bits/stdc++.h>
using namespace std;
const int Ni = 40005;
const int MAX = 1<<26;
struct Edge{
    int u,v,c;
    int next;
}edge[3*Ni];
int n,m,cnt,head[Ni],d[Ni],sp,tp,heads[Ni];//原點,匯點
int l,r;
void add(int u,int v,int c){
    edge[cnt].u=u; edge[cnt].v=v; edge[cnt].c=c;
    edge[cnt].next=head[u]; head[u]=cnt++;

    edge[cnt].u=v; edge[cnt].v=u; edge[cnt].c=0;
    edge[cnt].next=head[v]; head[v]=cnt++;
}
int bfs(){
    queue <int> q;
    memset(d,-1,sizeof(d));
    d[sp]=0;
    q.push(sp);
    while(!q.empty()){
        int cur=q.front();
        q.pop();
        for(int i=head[cur];i!=-1;i=edge[i].next){
            int u=edge[i].v;
            if(d[u]==-1 && edge[i].c>0){
                d[u]=d[cur]+1;
                q.push(u);
            }
        }
    }
    return d[tp] != -1;
}
int dfs(int a,int b){
    int r=0;
    if(a==tp)return b;
    for(int i=heads[a];i!=-1 && r<b;i=edge[i].next)
    {
        int u=edge[i].v;
        if(edge[i].c>0 && d[u]==d[a]+1)
        {
            int x=min(edge[i].c,b-r);
            heads[a]=i;
            x=dfs(u,x);
            r+=x;
            edge[i].c-=x;
            edge[i^1].c+=x;
        }
    }
    if(!r)d[a]=-2;
    return r;
}

int dinic(int sp,int tp){
    int total=0,t;
    while(bfs()){
        memcpy(heads,head,sizeof(head));
        while(t=dfs(sp,MAX))
        total+=t;
    }
    return total;
}
int main(){
    int i,u,v,k,cas=0;
    while(~scanf("%d%d%d",&n,&m,&k)){
        scanf("%d%d",&l,&r);
        cnt=0;
        memset(heads,-1,sizeof(heads));
        memset(head,-1,sizeof(head));
        sp=n+m+1,tp=n+m+2;   //附加源,附加匯
        int s=n+m+3,t=n+m+4;
        for(int i=1;i<=k;i++){
            scanf("%d%d",&u,&v);
            add(u,v+n,1);
        }
        for(int i=1;i<=n;i++){
            add(s,i,r-l);  //源點連向所有左側點,修改流量為R-L
            add(sp,i,l);   //附加源連向所有左側點,流量為L,表示必要弧
            add(s,tp,l);    //源點連向附加匯
        }
        for(int i=1;i<=m;i++){
            int aim=i+n;
            add(aim,t,r-l);   //右側點連向匯點,修改流量為R-L
            add(aim,tp,l);    //附加源連向匯點
            add(sp,t,l);     //右側點連向附加匯,流量為L,表示必要弧
        }
        add(t,s,MAX);
        printf("Case %d: ",++cas);
        int ans=dinic(sp,tp); //求附加源到附加匯的最大流,若滿足
        if(ans==(n+m)*l) printf("Yes\n");//附加源到附加匯的所有弧都滿流,則有可行流
        else printf("No\n");
    }
    return 0;
}