有源汇上下界可行流(最大流/最小流)

以无源汇上下界可行流为基础,如果有源点汇点呢,源点汇点是不必平衡的,我们要转变成无源汇的,那么源点流出的就要有一个汇点流入源点,同理也需要汇点流出,那很简单了,从当前汇点引一条边到当前源点,容量为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("
");
    }
}
原文地址:https://www.cnblogs.com/DF-yimeng/p/9726866.html