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

版权声明:本博客内容基本为原创,如有问题欢迎联系,转载请注明出处 https://blog.csdn.net/qq_41955236/article/details/82631008

题目链接:https://nanti.jisuanke.com/t/31447

题意:

       给出一张二分图,初始每个节点的度数都为零。选择若干条边,使得每个节点的度数范围再[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;
}

再贴一个zkw的最大流,据说是适用于稠密图

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<stdlib.h>
#include<queue>
using namespace std;
typedef long long ll;
ll N,M,K;

namespace MCMF {
    ll INF=1<<29;
    const ll MX=5010;
    ll S, T;//源点,汇点
    ll erear, n;
    ll st, en, maxflow, mincost;
    bool vis[MX];
    ll Head[MX], cur[MX], dis[MX];
    const ll ME = 1e5 + 5;//边的数量

    queue <int> Q;
    struct Edge
    {
        ll v, cap, cost, nxt, flow;
        Edge() {}
        Edge(ll a, ll b, ll c, ll d)
        {
            v = a, cap = b, cost = c, nxt = d, flow = 0;
        }
    } E[ME], SE[ME];

    void init(ll _n)
    {
        n = _n, erear = 0;
        for(ll i = 0; i <= n; i++) Head[i] = -1;
    }
    void addedge(ll u, ll v, ll cap, ll cost)
    {
        E[erear] = Edge(v, cap, cost, Head[u]);
        Head[u] = erear++;
        E[erear] = Edge(u, 0, -cost, Head[v]);
        Head[v] = erear++;
    }
    bool adjust() {
        ll v, min = INF;
        for(ll i = 0; i <= n; i++)
        {
            if(!vis[i]) continue;
            for(ll j = Head[i]; ~j; j = E[j].nxt)
            {
                v = E[j].v;
                if(E[j].cap - E[j].flow)
                {
                    if(!vis[v] && dis[v] - dis[i] + E[j].cost < min)
                    {
                        min = dis[v] - dis[i] + E[j].cost;
                    }
                }
            }
        }
        if(min == INF) return false;
        for(ll i = 0; i <= n; i++)
        {
            if(vis[i])
            {
                cur[i] = Head[i];
                vis[i] = false;
                dis[i] += min;
            }
        }
        return true;
    }
    ll augment(ll i, ll flow)
    {
        if(i == en)
        {
            mincost += dis[st] * flow;
            maxflow += flow;
            return flow;
        }
        vis[i] = true;
        for(ll j = cur[i]; j != -1; j = E[j].nxt)
        {
            ll v = E[j].v;
            if(E[j].cap == E[j].flow) continue;
            if(vis[v] || dis[v] + E[j].cost != dis[i]) continue;
            ll delta = augment(v, std::min(flow, E[j].cap - E[j].flow));
            if(delta)
            {
                E[j].flow += delta;
                E[j ^ 1].flow -= delta;
                cur[i] = j;
                return delta;
            }
        }
        return 0;
    }
    void spfa()
    {
        ll u, v;
        for(ll i = 0; i <= n; i++)
        {
            vis[i] = false;
            dis[i] = INF;
        }
        Q.push(st);
        dis[st] = 0; vis[st] = true;
        while(!Q.empty())
        {
            u = Q.front(), Q.pop(); vis[u] = false;
            for(ll i = Head[u]; ~i; i = E[i].nxt)
            {
                v = E[i].v;
                if(E[i].cap == E[i].flow || dis[v] <= dis[u] + E[i].cost) continue;
                dis[v] = dis[u] + E[i].cost;
                if(!vis[v])
                {
                    vis[v] = true;
                    Q.push(v);
                }
            }
        }
        for(ll i = 0; i <= n; i++)
        {
            dis[i] = dis[en] - dis[i];
        }
    }
    ll zkw(ll s, ll t, ll &ret_flow)
    {
        st = s, en = t;
        spfa();
        mincost = maxflow = 0;
        for(ll i = 0; i <= n; i++)
        {
            vis[i] = false;
            cur[i] = Head[i];
        }
        do {
            while(augment(st, INF))
            {
                memset(vis, false, n * sizeof(bool));
            }
        } while(adjust());
        ret_flow = maxflow;
        return mincost;
    }
    /*
    void out(ll pp)
    {
        for(ll i=0;i<pp;i+=2)
        {
            if(E[i].flow==E[i].cap)printf("%d ",i/2+1);
        }
        printf("\n");
    }
    */
}
int main()
{
    ll kase=1;
    while(scanf("%lld%lld%lld",&N, &M,&K)!=EOF)
    {
        ll source=N+M+1;
        ll sink=source+1;
        ll L,R;
        scanf("%lld%lld",&L,&R);
        MCMF::init(N+M+2);

        while (K--)
        {
            ll u,v;
            scanf("%lld%lld",&u,&v);
            MCMF::addedge(u,v+N,1,0);

        }
        for (ll i=1;i<=N;i++)
        {
            MCMF::addedge(source,i,L,-1);
            if (R>L)
            MCMF::addedge(source,i,R-L,0);
        }
        for (ll i=1;i<=M;i++)
        {
            MCMF::addedge(i+N,sink,L,-1);
            if (R>L)
            MCMF::addedge(i+N,sink,R-L,0);
        }
        ll cost, flow;//最小费用 最大流
        cost=-MCMF::zkw(source, sink, flow);
        //printf("cost:%lld\n",cost);
        printf("Case %d: ",kase++);
        if(cost==(N+M)*L) printf("Yes\n");
        else printf("No\n");
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_41955236/article/details/82631008