zoukankan      html  css  js  c++  java
  • 有源汇上下界可行流(最大流/最小流)

    以无源汇上下界可行流为基础,如果有源点汇点呢,源点汇点是不必平衡的,我们要转变成无源汇的,那么源点流出的就要有一个汇点流入源点,同理也需要汇点流出,那很简单了,从当前汇点引一条边到当前源点,容量为inf即可,然后跑无源汇有上下界可行流就好啦

    这样跑出来一个后,如何调整到最大流 / 最小流呢?

    求最大流应该好做一点,从s 到 t的残流网络,跑一跑最大流就好了,最后的可行流最大流量就是 : 最初可行流流量 + s - t残流最大流流量

    稍微整理一下:
    有源汇上下界可行流:
    ·根据题意建图

    ·汇点 到 源点 连入容量无穷大的边 转变为无源汇上下界可行流(循环流)

    ·添加附加流 跑Dinic

    ·得到基础可行流

    ·删除附加流的边,依照初始源点汇点和残流网络再跑DInic

    ·得到最后的有源汇上下界可行最大流 = 第一次Dinic后从初始汇点留到源点的流量 + 残流网络最大流的流量

    ·每条边的流量 = 最低限流  +  对应逆向边的流量

    例题ZOJ3229

    n天 m个人
    每个人至少gx张
    每天 有C个不同目标 且至多排 D张
    C个目标 : 目标号 必须要拍l r张

    依据输入建完图后,就是典型的有源汇上下界可行流了

    先来看一下经典的初始化模板

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <queue>
    #define inf (1 << 28)
    using namespace std;
    const int maxn = 2005,maxm=1e5+1e2;
    //前向星
    struct node{
        int to,val,lid,pre;
    }e[maxm];
    int id[maxn],cnt = 0;
    //dinic
    int cur[maxn];
    int flor[maxn];//BFS的分层处理
    int totid;
    int n,m;//基础变量
    
    int upflow[maxn];//差流(可升流)
    int retf[maxm];//结果
    int low[maxm];//下界
    
    void init()
    {
        memset(id,-1,sizeof(id));
        memset(upflow,0,sizeof(upflow));
        cnt = 0;
    }
    //网络流加边
    void add(int from,int to,int val,int lid)
    {
        e[cnt].lid = lid;
        e[cnt].to = to;
        e[cnt].val = val;
        e[cnt].pre = id[from];
        id[from] = cnt++;
        swap(from,to);
        e[cnt].lid = lid;
        e[cnt].to = to;
        e[cnt].val = 0;
        e[cnt].pre = id[from];
        id[from] = cnt++;
    }
    //Dinic
    //bfs分层
    bool bfs(int s,int t)
    {
        memset(flor,0,sizeof(flor));
        flor[s] = 1;
        queue<int> q;
        while(q.size())q.pop();
        q.push(s);
    
        while(q.size())
        {
            int now = q.front();
            q.pop();
    
            for(int i = id[now];~i;i = e[i].pre)
            {
                int to = e[i].to;
                int val = e[i].val;
                if(val > 0 && flor[to] == 0)
                {
                    flor[to] = flor[now] + 1;
                    //printf("%d flor = %d
    ",to,flor[to]);
                    q.push(to);
                    if(to == t)return 1;
                }
            }
        }
        return 0;
    }
    int dfs(int s,int t,int value)
    {
        //printf("s t value = ::: %d %d %d
    ",s,t,value);
        if(s == t || value == 0)return value;
        int ret = value,a;
        for(int &i = cur[s];~i;i = e[i].pre)
        {
            int to = e[i].to;
            int val = e[i].val;
            //printf("to = %d val = %d flornow = %d florto = %d
    ",to,val,flor[s],flor[to]);
            if(flor[to] == flor[s] + 1 && (a = dfs(to,t,min(ret,val))))
            {
                //printf("a = %d
    ",a);
                e[i].val -= a;
                e[i^1].val += a;
                ret -= a;
                if(ret == 0)break;
            }
        }
        if(ret == value)flor[s] = 0;
    
        return value - ret;
    }
    int dinic(int s,int t)
    {
        int ret = 0;
        while(bfs(s,t))
        {
            memcpy(cur,id,sizeof(id));
            ret += dfs(s,t,inf);
            //cout<<ret<<endl;
        }
        return ret;
    }
    

     对于已有的边我要记录流量差,以提供给附加流增流依据

    void addflow(int from,int to,int low,int up,int lid)
    {
        upflow[from] -= low;
        upflow[to] += low;
        add(from,to,up-low,lid);
    }
    

     对于我们新增加的源点汇点就不用啦

    剩下的操作我就一main到底了…………

    根据题意建边,存储最低限流

    init();
            s = 0;t = n + m + 1;
            ss = t + 1;
            tt = t + 2;
            totid = 0;
            for(int i = 1;i <= m;++i)
            {
                scanf("%d",&peoplelim);
                addflow(n+i,t,peoplelim,inf,0);
            }
            int targetnum,daylim;
            for(int i = 1;i <= n;++i)//天
            {
                scanf("%d%d",&targetnum,&daylim);
                addflow(s,i,0,daylim,0);
                for(int j = 1;j <= targetnum;j++)
                {
                    int targetid,l,r;
                    scanf("%d%d%d",&targetid,&l,&r);
                    addflow(i,n+1+targetid,l,r,++totid);
                    low[totid] = l;
                }
            }
    

     转变无源汇,求出基本可行流

    int sum = 0;
    
            for(int i = s;i <= t;++i)
            {
                if(upflow[i] < 0)
                    add(i,tt,-upflow[i],0);
                else
                {
                    sum += upflow[i];
                    add(ss,i,upflow[i],0);
                }
            }
            add(t,s,inf,0);
    

     存在基本可行流

    删除添加的边,在原来的有源汇图中跑一遍残流网络最大流

    if(dinic(ss,tt) == sum)
            {
    //删边 for(int i = id[ss];~i;i = e[i].pre) { e[i].val = e[i^1].val = 0; } for(int i = id[tt];~i;i = e[i].pre) { e[i].val = e[i^1].val = 0; } int baseflow = e[cnt-1].val; e[cnt - 1].val = e[cnt - 2].val = 0; printf("%d ",baseflow + dinic(s,t)); for(int now = 1;now <= m;++now) { for(int i = id[n+now];~i;i = e[i].pre) { int lid = e[i].lid; if(lid == 0)continue; if(i % 2 == 0)continue; retf[lid] = e[i].val + low[lid]; } } for(int i=1;i<=totid;++i)printf("%d ",retf[i]); } else { printf("-1 "); }

     完整代码:

    /**
    n天 m个人
    每个人至少gx张
    每天 有C个不同目标 且至多排 D张
    C个目标 : 目标号 必须要拍l r张
    **/
    
    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <queue>
    #define inf (1 << 28)
    using namespace std;
    const int maxn = 2005,maxm=1e5+1e2;
    //前向星
    struct node{
        int to,val,lid,pre;
    }e[maxm];
    int id[maxn],cnt = 0;
    //dinic
    int cur[maxn];
    int flor[maxn];//BFS的分层处理
    int totid;
    int n,m;//基础变量
    
    int upflow[maxn];//差流(可升流)
    int retf[maxm];//结果
    int low[maxm];//下界
    
    void init()
    {
        memset(id,-1,sizeof(id));
        memset(upflow,0,sizeof(upflow));
        cnt = 0;
    }
    //网络流加边
    void add(int from,int to,int val,int lid)
    {
        e[cnt].lid = lid;
        e[cnt].to = to;
        e[cnt].val = val;
        e[cnt].pre = id[from];
        id[from] = cnt++;
        swap(from,to);
        e[cnt].lid = lid;
        e[cnt].to = to;
        e[cnt].val = 0;
        e[cnt].pre = id[from];
        id[from] = cnt++;
    }
    //Dinic
    //bfs分层
    bool bfs(int s,int t)
    {
        memset(flor,0,sizeof(flor));
        flor[s] = 1;
        queue<int> q;
        while(q.size())q.pop();
        q.push(s);
    
        while(q.size())
        {
            int now = q.front();
            q.pop();
    
            for(int i = id[now];~i;i = e[i].pre)
            {
                int to = e[i].to;
                int val = e[i].val;
                if(val > 0 && flor[to] == 0)
                {
                    flor[to] = flor[now] + 1;
                    //printf("%d flor = %d
    ",to,flor[to]);
                    q.push(to);
                    if(to == t)return 1;
                }
            }
        }
        return 0;
    }
    int dfs(int s,int t,int value)
    {
        //printf("s t value = ::: %d %d %d
    ",s,t,value);
        if(s == t || value == 0)return value;
        int ret = value,a;
        for(int &i = cur[s];~i;i = e[i].pre)
        {
            int to = e[i].to;
            int val = e[i].val;
            //printf("to = %d val = %d flornow = %d florto = %d
    ",to,val,flor[s],flor[to]);
            if(flor[to] == flor[s] + 1 && (a = dfs(to,t,min(ret,val))))
            {
                //printf("a = %d
    ",a);
                e[i].val -= a;
                e[i^1].val += a;
                ret -= a;
                if(ret == 0)break;
            }
        }
        if(ret == value)flor[s] = 0;
    
        return value - ret;
    }
    int dinic(int s,int t)
    {
        int ret = 0;
        while(bfs(s,t))
        {
            memcpy(cur,id,sizeof(id));
            ret += dfs(s,t,inf);
            //cout<<ret<<endl;
        }
        return ret;
    }
    
    
    void addflow(int from,int to,int low,int up,int lid)
    {
        upflow[from] -= low;
        upflow[to] += low;
        add(from,to,up-low,lid);
    }
    int main()
    {
        int s,t;//初始源汇
        int ss,tt;//无源汇有上下界可行流种附加流的源汇
        int peoplelim;//每个人至少拍的张数
    
        while(~scanf("%d%d",&n,&m))
        {
            init();
            s = 0;t = n + m + 1;
            ss = t + 1;
            tt = t + 2;
            totid = 0;
            for(int i = 1;i <= m;++i)
            {
                scanf("%d",&peoplelim);
                addflow(n+i,t,peoplelim,inf,0);
            }
            int targetnum,daylim;
            for(int i = 1;i <= n;++i)//天
            {
                scanf("%d%d",&targetnum,&daylim);
                addflow(s,i,0,daylim,0);
                for(int j = 1;j <= targetnum;j++)
                {
                    int targetid,l,r;
                    scanf("%d%d%d",&targetid,&l,&r);
                    addflow(i,n+1+targetid,l,r,++totid);
                    low[totid] = l;
                }
            }
            //bound_flow();//有源汇上下界可行最大流
            int sum = 0;
    
            for(int i = s;i <= t;++i)
            {
                if(upflow[i] < 0)
                    add(i,tt,-upflow[i],0);
                else
                {
                    sum += upflow[i];
                    add(ss,i,upflow[i],0);
                }
            }
            add(t,s,inf,0);
            if(dinic(ss,tt) == sum)
            {
                for(int i = id[ss];~i;i = e[i].pre)
                {
                    e[i].val = e[i^1].val = 0;
                }
                for(int i = id[tt];~i;i = e[i].pre)
                {
                    e[i].val = e[i^1].val = 0;
                }
                int baseflow = e[cnt-1].val;
                e[cnt - 1].val = e[cnt - 2].val = 0;
                printf("%d
    ",baseflow + dinic(s,t));
                for(int now = 1;now <= m;++now)
                {
                    for(int i = id[n+now];~i;i = e[i].pre)
                    {
                        int lid = e[i].lid;
                        if(lid == 0)continue;
                        if(i % 2 == 0)continue;
                        retf[lid] = e[i].val + low[lid];
                    }
                }
                for(int i=1;i<=totid;++i)printf("%d
    ",retf[i]);
            }
            else
            {
                printf("-1
    ");
            }
            printf("
    ");
        }
    }
    
  • 相关阅读:
    Nightwatch的介绍
    reduce的用法及实例
    什么是声明式渲染?
    H5自带的表单验证
    Flex弹性布局
    JS中的forEach,for in,for of和for的遍历优缺点及区别
    将博客搬至CSDN
    9 外观模式(Facade)
    8 代理模式(Proxy)
    7 装饰模式(Decorator)
  • 原文地址:https://www.cnblogs.com/DF-yimeng/p/9726866.html
Copyright © 2011-2022 走看看