有源汇上下界可行流(最大流/最小流)
以无源汇上下界可行流为基础,如果有源点汇点呢,源点汇点是不必平衡的,我们要转变成无源汇的,那么源点流出的就要有一个汇点流入源点,同理也需要汇点流出,那很简单了,从当前汇点引一条边到当前源点,容量为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\n",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\n",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\n",to,val,flor[s],flor[to]);
if(flor[to] == flor[s] + 1 && (a = dfs(to,t,min(ret,val))))
{
//printf("a = %d\n",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\n",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\n",retf[i]);
}
else
{
printf("-1\n");
}
完整代码:
/**
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\n",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\n",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\n",to,val,flor[s],flor[to]);
if(flor[to] == flor[s] + 1 && (a = dfs(to,t,min(ret,val))))
{
//printf("a = %d\n",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\n",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\n",retf[i]);
}
else
{
printf("-1\n");
}
printf("\n");
}
}

浙公网安备 33010602011771号