【2018沈阳赛区网络预选赛J题】Fantastic Graph 【有上下界的网络流】

题意:

给出一张二分图,初始每个节点的度数都为零。选择若干条边,使得每个节点的度数范围再[L,R]范围内。每选一条边,边上两端的节点度数+1。

 

题解:

首先先学习什么是有上下界的网络流

源点与左边每个节点连[L,R]的边。右边每个节点与汇点连[L,R]的边。

左右两边按照题意连权值为1的边。

最后判断有无可行流即可。

#include<bits/stdc++.h>
using namespace std;
const int maxn=70005;
int sp,tp,cnt=1,head[maxn],nxt[maxn],to[maxn],cap[maxn],dis[1005],st,de,def[maxn],m,n,k;
inline int read(){
    int ans=0; char last=' ',ch=getchar();
    while(ch<'0' || ch>'9')last=ch,ch=getchar();
    while(ch>='0' && ch<='9')ans=ans*10+ch-'0',ch=getchar();
    if(last=='-')ans=-ans; return ans;
}
inline void addedge(int u,int v,int p){
    nxt[++cnt]=head[u],head[u]=cnt,to[cnt]=v,cap[cnt]=p;
    nxt[++cnt]=head[v],head[v]=cnt,to[cnt]=u,cap[cnt]=0;
}
inline bool bfs(){
    int u,e,v;
    queue<int> que;
    memset(dis,-1,sizeof(dis));
    que.push(sp),dis[sp]=0;
    while(!que.empty()){
        u=que.front(),que.pop();
        for(int e=head[u];e;e=nxt[e]){
            if(cap[e]>0&&dis[v=to[e]]==-1){
                dis[v]=dis[u]+1,que.push(v);
                if(v==tp) return true;
            }
        }
    }
    return false;
}
inline int dfs(const int &u,const int &flow){
    if(u==tp) return flow;
    int res=0,v,flw;
    for(int e=head[u];e;e=nxt[e]){
        if(cap[e]>0&&dis[u]<dis[v=to[e]]){
            flw=dfs(v,min(cap[e],flow-res));
            if(flw==0) dis[v]=-1;
            cap[e]-=flw,cap[e^1]+=flw;
            res+=flw;if(res==flow) break;
        }
    }
    return res;
}
inline int dinic(int sp,int tp){
    int ans=0;
    while(bfs()) {
        ans+=dfs(sp,1<<30);
    }
    return ans;
}
int main(){

    int k,cas=0;
    while(scanf("%d%d%d",&n,&m,&k)!=EOF)
    {

     cas++;
    int s,t,up,down,sum=0;
    cnt=1;

    memset(def,0,sizeof(def));
    memset(head,-1,sizeof(head));
    down=read() , up=read();

    st=n+m+1,de=n+m+2;
    for(int i=1;i<=n;i++){

        addedge(st,i,up-down);
        def[st]+=down,def[i]-=down;
    }
    for(int i=1 ; i<=m ; i++)
    {
        addedge(i+n,de,up-down);
        def[i+n]+=down,def[de]-=down;
    }
    for(int i=1 ; i<=k ; i++)
    {
        scanf("%d%d",&s,&t);
        addedge(s,t+n,1);

    }
    sp=n+3+m,tp=n+4+m;

    for(int i=1;i<=n+m+2;i++){
        if(def[i]>0) sum+=def[i],addedge(i,tp,def[i]);
        if(def[i]<0) addedge(sp,i,-def[i]);
    }
    addedge(de,st,1<<30);///变无源
    printf("Case %d: ",cas);
    if(dinic(sp,tp)==sum){

        cout<<"Yes"<<endl;
    }
    else cout<<"No"<<endl;
    }
    return 0;
}
View Code

还有一种贪心的算法,据说是数据太水可以过 , :贪心地删边后检查每个点的度即可

#include<stdio.h>
#include<string.h>
struct no
{
    int u,v;
}a[40001];
int sum[40001];
int main( )
{
    int n,m,k,L,R,cnt,cas=0;
    while(scanf("%d%d%d",&n,&m,&k)!=EOF)
    {
        memset(sum,0,sizeof(sum));
        cnt=0;

        scanf("%d%d",&L,&R);

        for(int i=1 ; i<=k ; i++)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            v+=n;
            a[cnt].u=u;
            a[cnt++].v=v;
            sum[u]++;
            sum[v]++;
        }

        for(int i=0 ; i<cnt ; i++)
        {
            if(sum[a[i].u]>R || sum[a[i].v]>R)
            {
                sum[a[i].u]--;
                sum[a[i].v]--;
            }
        }
        bool fa=0;
        for(int i=1 ; i<=n+m ; i++)
        {
            if(sum[i]<L||sum[i]>R)
            {
                fa=1;
                break;
            }
        }
        cas++;
        printf("Case %d: ",cas);
        if(fa)
        puts("No");
        else
        puts("Yes");
    }
}
View Code
原文地址:https://www.cnblogs.com/shuaihui520/p/9631704.html