无源汇有上下界可行流(也就是循环流)
模型:一个网络,求出一个流,使得每条边的流量必须>=Li且<=Hi,
每个点必须满足总流入量=总流出量(流量守恒)(这个流的特点是循环往复,无始无终)
可行流算法的核心是将一个不满足流量守恒的初始流调整成满足流量守恒的流
流量守恒,即每个点的总流入量=总流出量
如果存在一个可行流,那么一定满足每条边的流量都大于等于流量的下限
因此我们可以令每条边的流量等于流量下限,得到一个初始流,然后建出这个流的残量网络
(即:每条边的流量等于这条边的流量上限与流量下限之差)
这个初始流不一定满足流量守恒,因此最终的可行流一定是在这个初始流的基础上增大了一些边的流量使得所有点满足流量守恒
因此我们考虑在残量网络上求出一个另不满足流量守恒的附加流,
使得这个附加流和我们的初始流合并之后满足流量守恒
那么首先我们需要开一个数组A,A[i]表示i在初始流中的流入量-流出量的值
那么A[i]的正负表示流入量和流出量的大小关系,
A[i]>0,说明附加流的流入量要小于流出量
A[i]<0,说明附加流的流入量要大于流出量
所以A[i]=附加流流出量-流入量
下面就用A[i]表示初始流中i的流入量-流出量
但是dinic算法能够求的是满足流量守恒的有源汇最大流,
不能在原网络上直接求一个这样的无源汇且不满足流量守恒的附加流
注意到附加流是在原网络上不满足流量守恒的,这启发我们添加一些原网络之外的边和点,
用这些边和点实现“原网络上流量不守恒”的限制
具体地,如果一个点i在原网络上的附加流中需要满足流入量>流出量(A[i]<0),
那么我们需要给多的流入量找一个去处,因此我们建一条从i出发流量=|A[i]|的边
如果A[i]>0,也就是我们需要让附加流中的流出量>流入量,我们需要让多的流出量有一个来路,
因此我们建一条指向i的流量=|A[i]|的边.
当然,我们所新建的从i出发的边也要有个去处,指向i的边也要有个来路,
因此我们新建一个虚拟源点ss和一个虚拟汇点tt
(双写字母是为了和有源汇网络流中的源点s汇点t相区分)
新建的指向i的边都从ss出发,从i出发的边都指向tt
一个点要么有一条边指向tt,要么有一条边来自ss,
指向tt的边的总流量上限一定等于ss流出的边的总流量上限
因为每一条边对两个点的A[i]贡献一正一负大小相等,所以全部点的A[i]之和等于0,
即小于0的A[i]之和的绝对值=大于0的A[i]之和的绝对值.
如果我们能找到一个流满足新加的边都满流,这个流在原图上的部分就是我们需要的附加流
那么怎样找出一个新加的边都满流的流呢?
可以发现假如存在这样的方案,这样的流一定是我们所建出的图的ss-tt最大流,
所以跑ss到tt的最大流即可
如果最大流的大小等于ss出发的所有边的流量上限之和,则存在这样的附加流满足题意
最后,每条边在可行流中的流量=容量下界+附加流中它的流量(即跑完dinic之后所加反向边的权值)
来一道例题:
【zoj2314】Reactor Cooling
The terrorist group leaded by a well known international terrorist Ben Bladen is buliding a nuclear reactor to produce plutonium for the nuclear bomb they are planning to create. Being the wicked computer genius of this group, you are responsible for developing the cooling system for the reactor.
The cooling system of the reactor consists of the number of pipes that special cooling liquid flows by. Pipes are connected at special points, called nodes, each pipe has the starting node and the end point. The liquid must flow by the pipe from its start point to its end point and not in the opposite direction.
Let the nodes be numbered from 1 to N. The cooling system must be designed so that the liquid is circulating by the pipes and the amount of the liquid coming to each node (in the unit of time) is equal to the amount of liquid leaving the node. That is, if we designate the amount of liquid going by the pipe from i-th node to j-th as fij, (put fij= 0 if there is no pipe from node i to node j), for each i the following condition must hold:
fi,1+fi,2+…+fi,N = f1,i+f2,i+…+fN,i
Each pipe has some finite capacity, therefore for each i and j connected by the pipe must be fij <= cij where cij is the capacity of the pipe. To provide sufficient cooling, the amount of the liquid flowing by the pipe going from i-th to j-th nodes must be at least lij, thus it must be fij >= lij.
Given cij and lij for all pipes, find the amount fij, satisfying the conditions specified above.
This problem contains multiple test cases!
The first line of a multiple input is an integer N, then a blank line followed by N input blocks. Each input block is in the format indicated in the problem description. There is a blank line between input blocks.
The output format consists of N output blocks. There is a blank line between output blocks.
Input
The first line of the input file contains the number N (1 <= N <= 200) – the number of nodes and and M – the number of pipes. The following M lines contain four integer number each – i, j, lij and cij each. There is at most one pipe connecting any two nodes and 0 <= lij <= cij <= 10^5 for all pipes. No pipe connects a node to itself. If there is a pipe from i-th node to j-th, there is no pipe from j-th node to i-th.
Output
On the first line of the output file print YES if there is the way to carry out reactor cooling and NO if there is none. In the first case M integers must follow, k-th number being the amount of liquid flowing by the k-th pipe. Pipes are numbered as they are given in the input file.
Sample Input
2
4 6
1 2 1 2
2 3 1 2
3 4 1 2
4 1 1 2
1 3 1 2
4 2 1 2
4 6
1 2 1 3
2 3 1 3
3 4 1 3
4 1 1 3
1 3 1 3
4 2 1 3
Sample Input
NO
YES
1
2
3
2
1
1
分析:无源汇最大流模板
在判断是否可行是
可以直接写成:
也可以把ans==sout替换为:
这里写代码片
#include<cstdio>
#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
const int N=100001;
const int INF=0x33333333;
struct node{
int x,y,v,nxt;
};
node way[N];
int st[N],tot;
int n,m,a[N],s,t,ans,deep[N],sout;
bool p[N];
int bh[N],in[N];
void add(int u,int w,int z)
{
tot++;
way[tot].x=u;way[tot].y=w;way[tot].v=z;way[tot].nxt=st[u];st[u]=tot;
tot++;
way[tot].x=w;way[tot].y=u;way[tot].v=0;way[tot].nxt=st[w];st[w]=tot;
}
int bfs()
{
int i;
memset(p,1,sizeof(p));
memset(deep,0x33,sizeof(deep));
queue<int> q;
q.push(s);
p[s]=0;
deep[s]=1;
while (!q.empty())
{
int r=q.front();
q.pop();
for (i=st[r];i!=-1;i=way[i].nxt)
{
if (way[i].v&&p[way[i].y])
{
deep[way[i].y]=deep[r]+1;
p[way[i].y]=0;
q.push(way[i].y);
}
}
}
return !p[t];
}
int dfs(int now,int t,int limit)
{
if (now==t||!limit) return limit;
int i,f,flow=0;
for (i=st[now];i!=-1;i=way[i].nxt)
{
if (way[i].v&&deep[way[i].y]==deep[now]+1&&(f=dfs(way[i].y,t,min(limit,way[i].v))))
{
flow+=f;
limit-=f;
way[i].v-=f;
way[i^1].v+=f;
if (!limit) break;
}
}
return flow;
}
void doit()
{
ans=0;
while (bfs())
ans+=dfs(s,t,INF);
return;
}
int pd()
{
int i;
for (i=st[s];i!=-1;i=way[i].nxt)
if (way[i].v) return 0; //没流满
return 1;
}
int main()
{
int T;
scanf("%d",&T);
while (T--)
{
sout=0;
tot=-1;
memset(st,-1,sizeof(st));
memset(a,0,sizeof(a)); //别忘了附初始值
scanf("%d%d",&n,&m);
s=0;t=n+1;
for (int i=1;i<=m;i++)
{
int u,w,z1,z2;
scanf("%d%d%d%d",&u,&w,&z1,&z2);
in[i]=z1; //该水管的初始流量
a[w]+=z1; //该点的流入量
a[u]-=z1; //该点的流出量
add(u,w,z2-z1); //残量网络
bh[i]=tot; //i水管在残量网络中的反向边编号
}
for (int i=1;i<=n;i++)
{
if (a[i]>0) add(s,i,a[i]),sout+=a[i]; //sout记录s点的流出量
if (a[i]<0) add(i,t,-a[i]); //
}
doit();
if (ans==sout)
{
printf("YES\n");
for (int i=1;i<=m;i++)
printf("%d\n",in[i]+way[bh[i]].v);
}
else printf("NO\n");
}
return 0;
}