网络流算法和PASCAL程序文档格式.docx
- 文档编号:21371299
- 上传时间:2023-01-30
- 格式:DOCX
- 页数:25
- 大小:47.29KB
网络流算法和PASCAL程序文档格式.docx
《网络流算法和PASCAL程序文档格式.docx》由会员分享,可在线阅读,更多相关《网络流算法和PASCAL程序文档格式.docx(25页珍藏版)》请在冰豆网上搜索。
流守恒(FlowConservation):
除非u=s或u=t,否则Σ(w∈V)f(u,w)=0一结点的净流是零,除了“制造”流的源点和“消耗”流的汇点。
注意f(u,v)是由u到v的净流。
如果该图代表一个实质的网络,由u到v有4单位的实际流及由v到u有3单位的实际流,那么f(u,v)=1及f(v,u)=−1。
边的剩余容量(ResidualCapacity)是cf(u,v)=c(u,v)−f(u,v)。
这定义了以Gf(V,Ef)表示的剩余网络(ResidualNetwork),它显示可用的容量的多少。
留意就算在原网络中由u到v没有边,在剩余网络乃可能有由u到v的边。
因为相反方向的流抵消,减少由v到u的流等于增加由u到v的流。
扩张路径(AugmentingPath)是一条路径(u1,u2...uk),而u1=s、uk=t及cf(ui,ui+1)>
0,这表示沿这条路径传送更多流是可能的。
编辑本段求最大流算法
1、augmentpath,直译为“增广路”,其思想大致如下:
原有网络为G,设有一辅助图G'
,其定义为V(G'
)=V(G),E(G'
)初始值(也就是容量)与E(G)相同。
每次操作时从Source点搜索出一条到Sink点的路径,然后将该路径上所有的容量减去该路径上容量的最小值,然后对路径上每一条边<
u,v>
添加或扩大反方向的容量,大小就是刚才减去的容量。
一直到没有路为止。
此时辅助图上的正向流就是最大流。
我们很容易觉得这个算法会陷入死循环,但事实上不是这样的。
我们只需要注意到每次网络中由Source到Sink的流都增加了,若容量都是整数,则这个算法必然会结束。
寻找通路的时候可以用DFS,BFS最短路等算法。
就这两者来说,BFS要比DFS快得多,但是编码量也会相应上一个数量级。
增广路方法可以解决最大流问题,然而它有一个不可避免的缺陷,就是在极端情况下每次只能将流扩大1(假设容量、流为整数),这样会造成性能上的很大问题,解决这个问题有一个复杂得多的算法,就是预推进算法。
2、pushlabel,直译为“预推进”算法。
3、压入与重标记(Push-Relabel)算法
除了用各种方法在剩余网络中不断找增广路(augmenting)的Ford-Fulkerson系的算法外,还有一种求最大流的算法被称为压入与重标记(Push-Relabel)算法。
它的基本操作有:
压入,作用于一条边,将边的始点的预流尽可能多的压向终点;
重标记,作用于一个点,将它的高度(也就是label)设为所有邻接点的高度的最小值加一。
Push-Relabel系的算法普遍要比Ford-Fulkerson系的算法快,但是缺点是相对难以理解。
Relabel-to-Front使用一个链表保存溢出顶点,用Discharge操作不断使溢出顶点不再溢出。
Discharge的操作过程是:
若找不到可被压入的临边,则重标记,否则对临边压入,直至点不再溢出。
算法的主过程是:
首先将源点出发的所有边充满,然后将除源和汇外的所有顶点保存在一个链表里,从链表头开始进行Discharge,如果完成后顶点的高度有所增加,则将这个顶点置于链表的头部,对下一个顶点开始Discharge。
Relabel-to-Front算法的时间复杂度是O(V^3),还有一个叫HighestLabelPreflowPush的算法复杂度据说是O(V^2*E^0.5)。
我研究了一下HLPP,感觉它和Relabel-to-Front本质上没有区别,因为Relabel-to-Front每次前移的都是高度最高的顶点,所以也相当于每次选择最高的标号进行更新。
还有一个感觉也会很好实现的算法是使用队列维护溢出顶点,每次对pop出来的顶点discharge,出现了新的溢出顶点时入队。
Push-Relabel类的算法有一个名为gapheuristic的优化,就是当存在一个整数0<
k<
V,没有任何顶点满足h[v]=k时,对所有h[v]>
k的顶点v做更新,若它小于V+1就置为V+1。
cpp程序:
#include<
cstdio>
constlongmaxn=1000+10;
constlongmaxm=440000+100;
constlongmaxnum=100000000;
longn,m,s=0,tot=0,list[maxn],p[maxn],g[maxn],gg[maxn],dis[maxn];
boolmark[maxn];
structnode
{
longk,f,c,next,g;
}d[maxm<
<
1];
voidinsert(longu,longv,longc)
d[tot].k=v;
d[tot].f=0;
d[tot].c=c;
d[tot].next=g;
g=tot;
d[tot].g=tot+1;
tot++;
d[tot].k=u;
d[tot].c=0;
d[tot].next=g[v];
g[v]=tot;
d[tot].g=tot-1;
}
voidbfs()
longx,u,v,h,t;
for(longi=0;
i<
n;
i++)
dis=0;
h=0;
t=1;
list[1]=0;
编辑本段最小费用最大流
一.Ford和Fulkerson迭加算法.
基本思路:
把各条弧上单位流量的费用看成某种长度,用求解最短路问题的方法确定一条自V1至Vn的最短路;
在将这条最短路作为可扩充路,用求解最大流问题的方法将其上的流量增至最大可能值;
而这条最短路上的流量增加后,其上各条弧的单位流量的费用要重新确定,如此多次迭代,最终得到最小费用最大流.
迭加算法:
1)给定目标流量F或∞,给定最小费用的初始可行流=0
2)若V(f)=F,停止,f为最小费用流;
否则转(3).
3)构造相应的新的费用有向图W(fij),在W(fij)寻找Vs到Vt的最小费用有向路P(最短路),沿P增加流f的流量直到F,转
(2);
若不存在从Vs到Vt的最小费用的有向路P,停止.f就是最小费用最大流.
具体解题步骤:
设图中双线所示路径为最短路径,费用有向图为W(fij).
在图(a)中给出零流f,在图(b)中找到最小费用有向路,修改图(a)中的可行流,δ=min{4,3,5}=3,得图(c),再做出(c)的调整容量图,再构造相应的新的最小费用有向路得图(d),修改图(c)中的可行流,δ=min{1,1,2,2}=1,得图(e),以此类推,一直得到图(h),在图(h)中以无最小费用有向路,停止,经计算:
图(h)中最小费用=1*4+3*3+2*4+4*1+1*1+4*2+1*1+3*1=38
图(g)中最大流=5
最大流问题仅注意网络流的流通能力,没有考虑流通的费用。
实际上费用因素是很重要的。
例如在交通运输问题中,往往要求在完成运输任务的前提下,寻求一个使总运输费用最省的运输方案,这就是最小费用流问题。
如果只考虑单位货物的运输费用,那么这个问题就变成最短路问题。
由此可见,最短路问题是最小费用流问题的基础。
现已有一系列求最短路的成功方法。
最小费用流(或最小费用最大流)问题,可以交替使用求解最大流和最短路两种方法,通过迭代得到解决。
二.圈算法:
1)利用Ford和Fulkson标号算法找出流量为F(<
=最大流)的流f.
2)构造f对应的调整容量的流网络N'
(f).
3)搜索N'
(f)中的负费用有向图C(Floyd算法),若没有则停止,否则转(4).
4)在C上找出最大的循环流,并加到N上去,同时修改N'
(F)中C的容量,转(3).
具体解题步骤:
利用Ford和Fulkson标号算法,得网络的最大流F=5,见图(a),由图(a)构造相应的调整容量的流网络(b),图(b)中不存在负费用有向图,故停止.经计算:
图(b)中最小费用=4*1+3*1+1*1+3*3+4*2+1*1+4*1+2*4=38
图(a)中最大流为F=5
Type
szbest=record
v,l:
integer;
end;
szmap=record
c,f,w:
Var
map:
array[0..1000,0..1000]ofszmap;
best:
array[0..1000]ofszbest;
i,j,ans,n,k,a,b,c,d,s,t:
Functionfind:
boolean;
vari,j:
flag:
begin
fori:
=1tondobest.v:
=maxint;
best[s].v:
=0;
repeat
=true;
=1tondo
ifbest.v<
maxintthen
forj:
if(map[i,j].f<
map[i,j].c)and(best.v+map[i,j].w<
best[j].v)then
best[j].v:
=best.v+map[i,j].w;
best[j].l:
=i;
flag:
=false;
untilflag;
ifbest[t].v<
maxintthenexit(true)
elseexit(false);
Procedureupdata;
i:
=t;
whilei<
>
sdo
j:
=best.l;
inc(map[j,i].f);
map[i,j].f:
=-map[j,i].f;
=j;
inc(ans,best[t].v);
begin{main}
assign(input,'
g4.in'
);
reset(input);
assign(output,'
g4.out'
rewrite(output);
readln(n);
readln(a,b,c,d);
ifa+b+c+d>
0then
map[a,b].c:
=c;
map[b,a].c:
map[a,b].w:
=d;
map[b,a].w:
=-d;
untila+b+c+d=0;
s:
=1;
t:
=n;
ans:
whilefinddoupdata;
writeln(ans);
ifmap[i,j].f>
writeln(i,'
'
j,'
map[i,j].w,'
*'
map[i,j].f);
close(input);
close(output);
end.
三,ZKW费用流
费用流是网络流的一个很重要的组成部分,也是非常有用的一种图论模型,关于费用流的算法,流传比较广的主要是消圈和增广路算法,而近来炒得沸沸扬扬的ZKW算法也是一种非常优秀的算法,这里我就谈谈我对此算法的一些理解。
此算法是由ZKW大牛创立,主要思想仍然是找增广路,只是有了一些优化在里边。
原来我们找增广路主要是依靠最短路算法,如SPFA。
因此此算法的时间复杂度主要就取决于增广的次数和每次增广的耗费。
由于每一次找增广路是都是重新算一遍,这样似乎显得有些浪费,如果我们能够缩短找增广路的时间,那必定会大大地优化算法。
值得注意的是,在寻求最短路得过程中,设dis[i]为i到起点的距离,对于每一条由i引向j的边,必有dis[j]<
=dis[i]+map[i][j];
既然满足这样的恒等式,我们就可以借用KM算法的调整思想来寻求最短路,每次只走dis[j]=dis[i]+map[i][j]的路径,一旦不存在到达终点的路径,就扫描每一条边,找到最小的距离增加值,使得有至少一条新边被加入相等子图。
算法流程如下:
1.将dis数组清零,表示当前的相等子图内只有起点。
(如果存在负权边,必须要用spfa跑一遍,初始化出dis数组,下面的第二题就是这样的例子)。
2.深搜,如果到达终点,全部回退更改流量,再进行步骤2;
否则,转3。
3.修改dis的值,如果无法修改,结束程序,已经找到的答案,反之转2。
编辑本段有上下界的最大流
v上面讨论的网络流都只对每条弧都限定了上界(其实其下界可以看成0),现在给每条弧<
Vi,Vj>
加上一个下界限制Aij(即必须满足Aij≤fij)。
v弧上数字对第一个是上界,第二个是下界。
若是撇开下界不看,此图的最大流如图(a)所示,流量是6;
但若是加入了下界的限制,它的最大流量就只有5了。
编辑本段网络流算法
一、网络流的基本概念
先来看一个实例。
现在想将一些物资从S运抵T,必须经过一些中转站。
连接中转站的是公路,每条公路都有最大运载量。
如下图:
每条弧代表一条公路,弧上的数表示该公路的最大运载量。
最多能将多少货物从S运抵T?
这是一个典型的网络流模型。
为了解答此题,我们先了解网络流的有关定义和概念。
若有向图G=(V,E)满足下列条件:
1、有且仅有一个顶点S,它的入度为零,即d-(S)=0,这个顶点S便称为源点,或称为发点。
2、有且仅有一个顶点T,它的出度为零,即d+(T)=0,这个顶点T便称为汇点,或称为收点。
3、每一条弧都有非负数,叫做该边的容量。
边(vi,vj)的容量用cij表示。
则称之为网络流图,记为G=(V,E,C)
譬如图5-1就是一个网络流图。
1.可行流
对于网络流图G,每一条弧(i,j)都给定一个非负数fij,这一组数满足下列三条件时称为这网络的可行流,用f表示它。
1、每一条弧(i,j)有fij≤cij。
2、除源点S和汇点T以外的所有的点vi,恒有:
该等式说明中间点vi的流量守恒,输入与输出量相等。
3、对于源点S和汇点T有:
这里V(f)表示该可行流f的流量。
例如对图5-1而言,它的一个可行流如下:
流量V(f)=5。
2.可改进路
给定一个可行流f=。
若fij=cij,称<
vi,vj>
为饱和弧;
否则称<
为非饱和弧。
若fij=0,称<
为零流弧;
为非零流弧。
定义一条道路P,起点是S、终点是T。
把P上所有与P方向一致的弧定义为正向弧,正向弧的全体记为P+;
把P上所有与P方向相悖的弧定义为反向弧,反向弧的全体记为P-。
譬如在图5-1中,P=(S,V1,V2,V3,V4,T),那么
P+={<
S,V1>
<
V1,V2>
V2,V3>
V4,T>
}
P-={<
V4,V3>
给定一个可行流f,P是从S到T的一条道路,如果满足:
那么就称P是f的一条可改进路。
(有些书上又称:
可增广轨)之所以称作“可改进”,是因为可改进路上弧的流量通过一定的规则修改,可以令整个流量放大。
具体方法下一节会重点介绍,此不赘述。
3.割切
要解决网络最大流问题,必须先学习割切的概念和有关知识。
G=(V,E,C)是已知的网络流图,设U是V的一个子集,W=V\U,满足SU,TW。
即U、W把V分成两个不相交的集合,且源点和汇点分属不同的集合。
对于弧尾在U,弧头在W的弧所构成的集合称之为割切,用(U,W)表示。
把割切(U,W)中所有弧的容量之和叫做此割切的容量,记为C(U,W),即:
例如图5-1中,令U={S,V1},则W={V2,V3,V4,T},那么
C(U,W)=<
S,V2>
+<
V1,V3>
+<
V1,V4>
=8+4+4+1=17
定理:
对于已知的网络流图,设任意一可行流为f,任意一割切为(U,W),必有:
V(f)≤C(U,W)。
通俗简明的讲:
“最大流小于等于最小割”。
这是“流理论”里最基础最重要的定理。
整个“流”的理论系统都是在这个定理上建立起来的,必须特别重视。
下面我们给出证明。
网络流、可改进路、割切都是基础的概念,应该扎实掌握。
它们三者之间乍一看似乎风马牛不相干,其实内在联系是十分紧密的。
二、求最大流
何谓最大流?
首先它必须是一个可行流;
其次,它的流量必须达到最大。
这样的流就称为最大流。
譬如对图5-1而言,它的最大流如下:
下面探讨如何求得最大流。
在定义“可改进路”概念时,提到可以通过一定规则修改“可改进路”上弧的流量,可以使得总流量放大。
下面我们就具体看一看是什么“规则”。
对可改进路P上的弧<
,分为两种情况讨论:
第一种情况:
∈P+,可以令fij增加一个常数delta。
必须满足fij+delta≤cij,即delta≤cij–fij。
第二种情况:
∈P-,可以令fij减少一个常数delta。
必须满足fij-delta≥0,即delta≤fij
根据以上分析可以得出delta的计算公式:
因为P+的每条弧都是非饱和弧,P-的每条弧都是非零流弧,所以delta>
0。
容易证明,按照如此规则修正流量,既可以使所有中间点都满足“流量守恒”(即输入量等于输出量),又可以使得总的流量有所增加(因为delta>
0)。
因此我们对于任意的可行流f,只要在f中能找到可改进路,那么必然可以将f改造成为流量更大的一个可行流。
我们要求的是最大流,现在的问题是:
倘若在f中找不到可改进路,是不是f就一定是最大流呢?
答案是肯定的。
下面我们给出证明。
定理1可行流f是最大流的充分必要条件是:
f中不存在可改进路。
证明:
首先证明必要性:
已知最大流f,求证f中不存在可改进路。
若最大流f中存在可改进路P,那么可以根据一定规则(详见上文)修改P中弧的流量。
可以将f的流量放大,这与f是最大流矛盾。
故必要性得证。
再证明充分性:
已知流f,并且f中不存在可改进路,求证f是最大流。
我们定义顶点集合U,W如下:
(a)S∈U,
(b)若x∈U,且fxy<
cxy,则y∈U;
若x∈U,且fyx>
0,则y∈U。
(这实际上就是可改进路的构造规则)
(c)W=V\U。
由于f中不存在可改进路,所以T∈W;
又S∈U,所以U、W是一个割切(U,W)。
按照U的定义,若x∈U,y∈W,则fxy=cxy。
若x∈W,y∈U,则fxy=0。
所以,
又因v(f)≤C(U,W)
所以f是最大流。
得证。
根据充分性证明中的有关结论,我们可以得到另外一条重要定理:
最大流最小割定理:
最大流等于最小割,即maxV(f)=minC(U,W)。
至此,我们可以轻松设计出求最大流的算法:
step1.令所有弧的流量为0,从而构造一个流量为0的可行流f(称作零流)。
step2.若f中找不到可改进路则转step5;
否则找到任意一条可改进路P。
step3.根据P求delta。
step4.以delta为改进量,更新可行流f。
转step2。
step5.算法结
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 网络 算法 PASCAL 程序