网路流初步
什么是网络流?
请看下图:
这是一个有向图,每一条边都对应着两个数值“/”前面的称为流量,用
f(u,v)
表示,后面的称为容量,用
c(u,v)
表示,网络中有两个特殊的点,源点s和汇点t。
三个性质
(一)容量限制,对于所有原图中的变,均有
f(u,v)≤c(u,v)
(二)反对称性,为了方便计算,我们定
f(u,v)和f(v,u)
中最多只有一个是正数,可以都为负数,这样定义的话,从u往v运三个物品,又从v往u运两个,就相当于只从u往v运了一个。
(三)留守恒性,从源点流出的流量一定等于流入t的容量。
最大流问题
定义一个网络的流量为 ∑v∈Vf(s,v) ,最大流问题就是在满足三个性质的条件下,最大化 f 。
残量网络
为了方便算法的实现,我们定义一个残量网络,残量网络的容量
增广路
在一条从s到t的路线中,对于在这条路线中任意一条弧都有 r(u,v)>0 ,那么这就是
增广路算法
每次我们找一条增广路,并记录下在这些残量网络中最小的流量,然后将这些在这条增广路中的残量网络减去这个最小值,总流量加上这个值,同时也是满足这三个性质的,就像这样一直找,找到没有增广路为止。
举个例子
假设这是原图:
我们要求这个图的最大流,首先找一条增广路,
s→v1→v2→t,然后flow+min{9,10,8}
,此时flow=8,然后原图就变成下面这个样子:
然后我们又找到一条增广路,
s→v1→v2→v3→v4→t,flow加上min{(9−8),(10−8),8,7,4}
,此时flow=9。
接着再找一条增广路,
s→v3→v4→t
,flow加上
min{6,(7−1),(4−1)}
,此时flow=10。
此时在图中不存在增广路,算法结束。
最短增广路算法
每一次也是找一条增广路,只不过是最短的,其实时间复杂度基本是因为找增广路的时间,如果将找增广路的时间复杂度降下来,就好了。
距离标号
所谓距离标号,就是指某个点到汇点的经过的最少的弧的数量,设i点的距离标号为
di
,那么满足
di=dj+1
的(i,j)弧叫作允许弧,且增广时只走允许弧,那么无论怎么走都是最短路。初始化距离标号可以赋值为0。如何维护这个距离标号?
当在增广过程中发现从某点出发没有允许弧,那么就将这个点的距离标号设为从它出发的所有的弧的距离标号的最小值加1。
当前弧优化
对于每一个点我们都保存它的“当前弧”:初始化为第一条边,查找时直接从当前弧开始寻找,找到一条允许弧,就把它设为当前弧,改变距离标号的时候,将当前弧设为提供了最小标号的弧。
GAP优化
当一次距离标号时,出现了断层,那么就不存在增广路。
提醒
别看算法似乎很简单,有很多细节。
代码
now[i]表示当前弧。
const
maxn=1000+5;
var
flow,aug,i,j,k,a,b,c,n,m,tmp,min:longint;
pre,g,vh,now,dis:array[0..maxn] of longint;
h:array[0..maxn,0..maxn] of longint;
bz:boolean;
begin
readln(m,n);
for i:=1 to m do
begin
readln(a,b,c);
inc(h[a,b],c);
end;
for i:=1 to n do
now[i]:=1;
vh[0]:=n;
aug:=maxlongint;
i:=1;
while dis[1]<n do
begin
bz:=false;
g[i]:=aug;
for j:=now[i] to n do
begin
if (h[i,j]>0) and (dis[j]+1=dis[i]) then
begin
now[i]:=j;
bz:=true;
if h[i,j]<aug then
aug:=h[i,j];
pre[j]:=i;
i:=j;
if i=n then
begin
inc(flow,aug);
while i<>1 do
begin
dec(h[pre[i],i],aug);
inc(h[i,pre[i]],aug);
i:=pre[i];
end;
aug:=maxlongint;
end;
break;
end;
end;
if bz then
continue;
min:=n-1;
for j:=1 to n do
begin
if (h[i,j]>0) and (dis[j]<min) then
begin
k:=j;
min:=dis[j];
end;
end;
now[i]:=k;
dec(vh[dis[i]]);
if vh[dis[i]]=0 then
break;
dis[i]:=min+1;
inc(vh[dis[i]]);
if i<>1 then
begin
i:=pre[i];
aug:=g[i];
end;
end;
writeln(flow);
end.
the end
因为我水平有限,可能会有写错的地方,希望大家批评指正,多多包容,thank you for your patience.