收藏 分享(赏)

网络流网络流.ppt

上传人:lxhqcj 文档编号:7351285 上传时间:2019-05-15 格式:PPT 页数:89 大小:866KB
下载 相关 举报
网络流网络流.ppt_第1页
第1页 / 共89页
网络流网络流.ppt_第2页
第2页 / 共89页
网络流网络流.ppt_第3页
第3页 / 共89页
网络流网络流.ppt_第4页
第4页 / 共89页
网络流网络流.ppt_第5页
第5页 / 共89页
点击查看更多>>
资源描述

1、网络流,网络流网络流网络流网络流网络流网络流网络流网络流网络流网络流网络流,一些符号和定义,V表示整个图中的所有结点的集合. E表示整个图中所有边的集合. G = (V,E) ,表示整个图. s表示网络的源点,t表示网络的汇点. 对于每条边(u,v),有一个容量c(u,v) (c(u,v)=0) 如果c(u,v)=0,则表示(u,v)不存在在网络中。 如果原网络中不存在边(u,v),则令c(u,v)=0 对于每条边(u,v),有一个流量f(u,v).,一个简单的例子.网络可以被想象成一些输水的管道.括号内右边的数字表示管道的容量,左边的数字表示这条管道的当前流量.,网络流的三个性质,1、容量限

2、制: fu,v=cu,v 2、反对称性:fu,v = - fv,u 3、流量平衡: 对于不是源点也不是汇点的任意结点,流入该结点的流量和等于流出该结点的流量和。 结合反对称性,流量平衡也可以写成:只要满足这三个性质,就是一个合法的网络流.,最大流问题,定义一个网络的流量(记为|f|)=最大流问题,就是求在满足网络流性质的情况下,|f|的最大值。,残量网络,为了更方便算法的实现,一般根据原网络定义一个残量网络。其中r(u,v)为残量网络的容量。 r(u,v) = c(u,v) f(u,v) 通俗地讲:就是对于某一条边(也称弧),还能再有多少流量经过。 Gf残量网络,Ef表示残量网络的边集.,例1

3、,v1,t,s,v2,2,3,2,4,2,2,原网络 (a,b)表示(流量f,容量c),残量网络(如果网络中一条边的容量为0,则认为这条边不在残量网络中。r(s,v1)=0,所以就不画出来了。另外举个例子:r(v1,s) = c(v1,s) f(v1,s) = 0 (-f(s,v1) = f(s,v1) = 4.,图1,图2,例1,从残量网络中可以清楚地看到: 因为存在边(s,v2) = 3,我们知道从S到v2还可以再增加2单位的流量; 因为存在边(v1,t) = 2,我们知道从v1到t还可以再增加2单位的流量。,后向弧,其中像(v1,s)这样的边称为后向弧,它表示从v1到s还可以增加4单位的

4、流量。 但是从v1到s不是和原网络中的弧的方向相反吗?显然“从v1到s还可以增加4单位流量”这条信息毫无意义。那么,有必要建立这些后向弧吗?,v1,t,s,v2,2,3,2,4,2,2,为什么要建立后向弧,显然,例1中的画出来的不是一个最大流。 但是,如果我们把s - v2 - v1 - t这条路径经过的弧的流量都增加2,就得到了该网络的最大流。 注意到这条路径经过了一条后向弧:(v2,v1)。 如果不设立后向弧,算法就不能发现这条路径。 从本质上说,后向弧为算法纠正自己所犯的错误提供了可能性,它允许算法取消先前的错误的行为(让2单位的流从v1流到v2),为什么要建立后向弧,当然,可以把上面说

5、的情况当成特殊情况来处理。但使用后向弧可以使编程简单许多. 注意,后向弧只是概念上的,在程序中后向弧与前向弧并无区别.,增广路,增广路定义:在残量网络中的一条从s通往t的路径,其中任意一条弧(u,v),都有ru,v0。 绿色的即为一条增广路。,v1,t,s,v2,2,3,2,4,2,2,增广路算法,增广路算法:每次用BFS找一条最短的增广路径,然后沿着这条路径修改流量值(实际修改的是残量网络的边权)。当没有增广路时,算法停止,此时的流就是最大流。下面证明增广路算法的正确性.,将f,c,r的定义域扩展为点集,(在以后的叙述中,大写字母X,Y,S,T一般均表示点集) 点集间的流量和: f(X,Y)

6、 =即:X中的任意一点与Y中的任意一点组成的所有边上的流量之和.(边的方向为从X中的结点到Y中的结点) c,r等函数都有类似的定义.(点集间的容量和、点集间的残量网络容量和),结论1,1.f(X,X) = 0 (由流量反对称性) 2. f(X,Y) = -f(Y,X) (有流量反对称性) 3.f(X Y,Z) = f(X,Z) + f(Y,Z) (显然) 4.f(X,Y Z) = f(X,Y) + f(X,Z) (显然),最大流最小割定理,网络流中这三个条件等价(在同一个时刻): 1、f是最大流 2、残量网络中找不到增广路径 3、|f| = c(S,T),1、f是最大流 2、残量网络中找不到增

7、广路径 3、|f| = c(S,T),1 - 2证明: 显然.假设有增广路径,由于增广路径的容量至少为1,所以用这个增广路径增广过后的流的流量肯定要比f的大,这与f是最大流矛盾.,割的定义,一个割(S,T)由两个点集S,T组成. S+T = V s 属于 S. t 属于 T.提出割的定义,是为后面的证明作铺垫.,结论2(点集总流量为零),不包含s和t的点集,于它相关联的边上的流量之和为0. 证明: f(X,V) = (由流量平衡) = 0,结论3,任意割的流量等于整个网络的流量. 证明: f(S,T) = f(S,V) f(S,S) (由辅助定理1)= f(S,V) (由辅助定理1)= f(S

8、,V) + f(S s,V) (同上)= f(s,V) (由辅助定理2)= |f| (由|f|的定义),结论4,网络的流量小于等于任意一个割的容量.(注意这个与辅助定理3的区别.这里是容量) 即|f| = c(S,T) 证明: |f| = f(S,T) = (由定义)= (由流量限制)= c(S,T),2 - 3证明: 定义S = s v | 在残量网络中s到v有一条路径 ; T = V- S. 则 (S,T) 是一个割. |f| = f(S,T) (由辅助定理3) 而且,r(S,T) = 0. 假设不为0,则在残量网络中, 两个集合间必定有边相连,设在S的一端为v,在T的一端为u. 那么,s

9、就可以通过v到达u,那么根据S的定义,u就应该在S中.矛盾. 所以,|f| = f(S,T) = c(S,T) r(S,T) = c(S,T),1、f是最大流 2、残量网络中找不到增广路径 3、|f| = c(S,T),3 - 1证明: |f| 0),那么|f|+d肯定不能满足上面的条件.,1、f是最大流 2、残量网络中找不到增广路径 3、|f| = c(S,T),增广路算法的正确性,如果 最大流最小割定理不能从2推出3,那么存在这样一种可能性:尽管找不到增广路径了,但由于前面的错误决策,导致f还没有到达最大流,却不能通过修改当前流来得到最大流. 但由于最大流最小割定理的三个条件互相等价(1-

10、2,2-3,3-1), 一个流是最大流当且仅当它没有增广路径.,增广路算法的效率,设n = |V|, m = |E| 每次增广都是一次BFS,效率为O(m) 所以,总共的时间复杂度为O(m*f*) 其中f*为增广次数.怎么求f*?,f*,对于随机数据,f*的值与n比较接近.当m不太大也不太小时,f*的值较大.(我出随机数据的方法是:固定地为源点和汇点连上一些边,然后随机生成中间的边.中间的边保证边的两个端点的编号相差不太大.这与不少题目转成网络流后形成的图相似),f*的理论上界,考虑每一次增广,至少有一条边的r(u,v)值等于增广路径的流量.称这些边为临界边.增广之后,这条临界边就在残量网络中

11、消失. 假设一条临界边对应一次增广(事实上很难达到这样),令每条边成为临界边的次数为k(u,v),则有f* = O(m*k). k的上界?,k的上界,如果要让一条曾经的临界边(u,v)再次成为临界边,则必须有一条增广路径包含边(v,u).因为每次增广之后临界边就消失,要让他再次成为临界边至少要让他再次在残量网络中出现,即(v,u)要被增广. 结合上面的结论可以证明,当算法取的增广路总是残量网络中的最短路,任意一条边成为临界边的次数至多为n/2-1. 因此,增广路算法的效率为O(f*m) = O(km2) = O(nm2). (这只是个上界,一般情况是达不到的) 备注中为增广路算法我的代码实现。

12、数组u是残量网络的容量。,预流推进算法,下面将介绍一个更直观且时间效率更优的算法.,一个直观的想法,如果给你一个网络流,让你手算出它的最大流,你会怎么算? 一般人都会尝试着从源点出发,让每条边的流量尽可能得大,然后一点点往汇点推,直到遇到一条比较窄的弧,原先的流量过不去了,这才减少原先的流量.,v1,t,s,v2,(0,2),(4,4),(0,4),(3,3),(0,2),例2.一个直观的想法,大致的思路:从源点出发,逐步推进。 称当前状态下不满足流量平衡的结点为“溢出的结点”.(对于结点u,f(V,u) 0 ) 令e(u) = f(V,u),称为u点的赢余,直观地描述,就是“流入的比流出的多

13、多少”。e(v1)=4,e(v2)=3。不断将溢出的结点中的赢余往后继点推进,直到赢余都聚集在t.,如果多推了一些流量, 我们可以再把它推回来. (如e(v2)=3,但这3个单位的赢余已经没地方去了,只能推回来.)(沿着后向弧)这副图是原网络而不是残量网络,因此没把后项弧画出来),例2.一个直观的想法,程序没有全局观?!,此时e(v2)=3.正确的回推法是往(v2,s)推1,往(v2,v1)推2,然后使得这2个单位的赢余可以从(v1,t)推到t上。 但程序没有全局观,它万一往(v2,s)推了3个单位怎么办?我们总不能尝试所有的可能性吧,那样就变成搜索了.,引导机制,把流推错可能导致产生的流不是

14、最大流.我们需要有一个能引导流的推进方向的机制,当它发现我们先前的推进是错误的时候,能沿着正确的后向弧回推回来. 由于建立了后向弧,正推与回推在程序中并无却别,都是在推残量网络中的一条边.,高度标号的引导作用,高度标号就是这样的一个引导机制. 我们规定,如果一个结点溢出了,那么他的多余的流量只能流向高度标号比自己低的结点.(“水往低处流”) 当然,高度标号不可能事先知道往哪些方向推才是正确的.它将按情况动态改变自己的值,从而正确地引导流向.,重标号操作,当一个结点有赢余(溢出了), 周围却没有高度比它低的结点时候,我们就用重标号操作使它的标号上升到比周围最低的结点略高一点,使他的赢余能流出去.

15、 赢余千万不能困在某个结点里.对于任意一个非源非汇的结点,有赢余就意味着它不满足流量平衡,也就意味着整个网络流不是一个真正合法的网络流。,重标号操作,对于例2的这种情况,v2中过多的赢余最终会沿着(v2,v1)、(v2,s)流回去(虽然他们一开始流错了方向,但后来又被回推,等于说是被改正了)。只有当非源非汇的结点中的赢余全部流到汇点或流回源点后,这个流才重新合法。,高度函数,高度函数h(v)返回一个v的高度标号。 高度函数有三个基本条件:h(s) = |V| h(t) = 0 对于Ef(残量网络)中的每一条边(u,v),(r(u,v)0)h(u) 0,那就表示从u到v还可以增加流量,那h(u)

16、就应该比h(v)高才对.的确,我们后面还将规定,只有在h(u)h(v)的时候才能应用推进操作(将一个结点的盈余推进到另一个结点的操作).而高度函数为了满足其合法性,还要满足上述的这三个条件.后面我们将利用这三个条件证明预流推进算法的正确性。,高度函数的条件的实质,h(u) = h(v)+1.这个条件实质上是要求高度不能下降的太快,即水只能在高度相差不多的地方缓缓流过,不能像瀑布一样从很高的地方流到很低的地方。(否则就有流错的危险) 这和A*算法中的启发函数必须“相容”的条件类似。h函数的缓慢下降,保证了算法的正确性。后面我们将看到这个条件的作用.,两个关键操作,推进操作(将一个结点的盈余推到另

17、一个结点)重标号操作(更改一个结点的高度值,使其的盈余能朝着更多的地方流动),推进操作,使用对象:一条边(u,v) 使用条件: e(u)0,r(u,v)0, h(u) = h(v)+1 (u溢出,(u,v)在残量网络中,两者的高度差为1) 推进量为e(u)与r(u,v)的最小值。 推进时同时更改相关的r与e的值。,推进操作 伪代码,Procedure Push(u,v) X min e(u), r(u,v) Dec(r(u,v), x) Inc(r(v,u), x) Dec(e(u), x) Inc(e(v), x),重标号操作,使用对象: 一个结点u 使用条件: 结点u溢出;残量网络中周围所

18、有的点的高度都不比它低。 Relabel(u) u(u) = min h(v) | (u,v)是残量网络总的边 + 1 使用了重标号操作后,至少存在一个(u,v)满足h(u)=h(v)+1.,预流初始化(Init-Preflow),一开始的时候,我们要让和源点s相关连的边都尽可能的充满。但由于s没有溢出,不符合推进操作的使用条件,我们需要另写一段初始化的代码。还得做的一件事是初始化高度函数. h(s) = n h(v) = 0 (vs) 对于所有与s相关联的点v, Inc( e(v), c(s,v) ), Dec( e(s), c(s,v) ) 将边(s,v)反向,变成(v,s) (在残量网络

19、中)。 初始化过后,e(s)变成负数。,结论5,对于一个溢出的结点,两个关键操作(推进和重标号)能且只能应用一个。 证明:对于一个溢出的结点u,和所有与他相关联的点v( (u,v)在残量网络中存在),必然有h(u) = h(v) + 1.(由高度函数的定义). 根据v分成两种情况:1).所有v都有h(u)h(v)+1 2).至少存在一个v,使得h(u)=h(v)+1. 而1)2)互为否命题,不能同时成立或同时不成立.那么1)对应重标号,2)对应推进,两者必能应用一个且只能应用一个.,一般的预流推进算法,由辅助定理5,得到了一个一般的预流推进算法.(好短) Init-Preflow While

20、存在一个溢出的结点 选一个结点,应用相应的关键操作(推进或重标号). 当不存在溢出结点时(s,t不算),算法结束,得到一个可行流,并且还是最大流.,预流推进算法的正确性,预流只是不满足流量平衡,网络流的前两条性质-容量限制和反对称性它还是满足的.当不存在溢出结点时,流量平衡也满足了. 所以,当算法结束时,我们得到一个可行流(合法流). 为什么他是一个最大流呢? 下面先看几个结论:,结论6(结点高度永不下降),只有重标号操作能更改结点的高度标号. 在重标号操作应用前,必有h(u) = h(u) + 1. 所以,在重标号操作后,高度标号至少+1.,结论7,在算法执行过程中,h始终是一个合法的高度函

21、数.(满足那三个条件) 1).考察一个被重标号的结点u. 设(u,v)存在于Ef,v0是所有v中h最小的一个. H(u)=h(v0)+1,满足h(u)=h(v0)+1,而h(v0) = h(v),所以 h(u)=h(v)+1. 设(w,u)存在于Ef,则h(w)=h(u)+1=h(u)+1.仍旧满足.,结论7,在算法执行过程中,h始终是一个合法的高度函数.(满足那三个条件) 2).考察一个被推进的边(u,v). (v,u)可能是在这次推进之后才出现在Ef中.它的出现使得新增了一个限制条件:h(v)=h(u)+1.不过,这显然是满足的,因为推进操作的使用条件是h(u)=h(v)+1.那么h(v)

22、=h(u)-1 = h(u)+1,结论8(预流中无增广路),当h是一个合法的高度函数时,Gf中始终不存在增广路.(这个定理展示了h的条件的重要性和巧妙性) 证明:假设存在增广路p=(v0,v1,vk),其中v0=s,vk=t.因为增广路径中无重复点,k+1=|V|,即k|V|.,结论8(预流中无增广路),相加得: h(s)=h(t)+k=0+k=k 而k|V|,所以 h(s)|V|. 而根据定义,h(s)=|V|.矛盾.,预流推进算法的正确性,当有溢出结点时,根据结论5,必定可以在它上面施加一个操作. 当算法停止时,因为无溢出结点,所以当前流是一个合法流,而根据结论8,Gf中始终不存在增广路.

23、根据最大流最小割定理,当Gf中不存在增广路时,当前流是最大流. (算法执行了一半时虽然也没有增广路,但由于它不是一个合法流,前面的诸多定理都不成立). 算法的最优性的保证者: 对于所有在Ef中的(v,u), 均有h(v)=h(u)+1,更好的预流推进算法,前面的一般预流推进算法可以实现为O(n4).其瓶颈是非饱和推进.(非饱和推进是指在推进之后仍旧没有使(u,v)消失的推进.) 通过恰当地安排关键操作的顺序,可以使总的推进(主要是非饱和推进)和重标号的次数减少.接下来的relabel-to-front算法就用了这个思想.,Relabel-to-front,relabel-to-front算法维

24、护一个结点列表,然后依次检查列表中的结点.检查的过程就是:一口气将所有的赢余推给周围的人.如果在检查的时候这个结点被relabel了,那么他就被移到整个列表的最首部,并且重新从列表首部开始检查结点. 通过这样恰当地安排操作顺序(一次性把某个结点所有的赢余全部推掉),复杂度降到了O(n3).,一些定义,如果满足下面的两个条件,称(u,v)为可行弧: r(u,v)0 h(u) = h(v)+1 可行边集Ef,h:所有由可行弧组成的集合。 可行网络Gf,h = (V,Ef,h),结论9,10,结论9:可行网络中无环.(和结论8的证明类似,弄一堆式子然后叠加一下,导出矛盾)结论10:推进操作永远不会新

25、增可行弧,却可能使原有的可行弧消失.(根据可行弧的定义显然),结论11,在u被重标号之后: 1).至少有一条可行弧离开u.显然.设v0是u的邻居中h值最小的那一个,则(u,v0)必定是一条可行弧. 2).不可能有可行弧进入u.假设有一条(w,u).则h(w) = h(u) + 1.根据辅助定理6,relabel操作至少将结点的h+1,所以h(w) h(u) + 1.根据高度函数必须满足的条件,(w,u)在relabel前不在Ef中.而relabel操作只改变可行网络不改变残量网络,(w,u)不可能在relabel前存在于Ef而之后就不存在.,当前弧,每个结点有一个邻居列表和有一个“当前弧”的指

26、针,保存当前检查到邻居列表中的哪一条弧了。初始化时,“当前弧”指向与该结点相连的第一条边.邻居列表保存的是所有可能成为可行弧的弧.当再次调用检查操作时,可以从上一次检查了一半的地方继续检查.具体请看下面检查操作的伪代码:,检查操作,Check(u) While e(u)0 do If current(u)degree(u) then /当没有可行弧可以推进,该结点却仍旧有赢余时,重标号. Relabel(u) Current(u) = 1 Else If (u,current(u) 是一条可行弧 then Push(u,current(u) /push了之后就不能增加 current(u)的值

27、.因为这如果是一次非饱和推进,那再下一次检查时还是可以沿着这条弧做推进. Else Inc(current(u),当前弧的正确性,Current是全局变量,当某次Check操作结束时他的值并没有被清空. 比如结点u有10个邻居,上次检查到第7个,那再一次Check(u)的时候就只要从第7个开始检查就可以了。 为什么再一次检查的时候不要检查第1-6条边了?能否证明在再一次检查的时候他们一定不是可行弧?,当前弧的正确性,在relabel-to-front算法中,relabel只被Check调用. 当“当前弧”移动时,移动前它指向的那条弧一定是不可行的.而推进操作不能创造可行弧.只有relabel可

28、以.两次Check之间没有relabel操作.所以原先的不可行的弧在第二次Check之前一直是不可行的.,Relabel-to-front,Init-Preflow 初始化结点(除s,t)列表L(任何顺序均可) 令所有u,Current(u) = 1 u HeadL While u nil do Old-height h(u) Check(u) If h(u) old-height then 将u移到L首部 /如果h(u)比原先的h高了,说明被relabel,移到队首. u next(u),图例 (初始状态.结点下方数字为赢余,N显示的是邻居列表,N中红色的是当前弧指针所在的位置.),图例:x

29、被检查并重标号,并被提到L的首部(等于没提).注意当前弧的指针移到了t. x的所有赢余推给了y和t.,S -26,x 0,y 19,z 0,t 7,6543210,(12/12),(14/14),(7/16),(0/7),(5,5),(0,8),(0,10),图例 :y正在被检查.将8单位的赢余推给z之后还是有剩余.,S -26,x 0,y 11,z 8,t 7,6543210,(12/12),(14/14),(7/16),(0/7),(5,5),(8,8),(0,10),图例 :一次必须把赢余全部推光.所以y被重标号,当前弧指针从头开始查找,找到(y,x)这条可行弧之后进行推进.实际上是把多

30、推的赢余还给了x.因为h(u)=h(v)+1的保证,它没有把赢余错推给s.,S -26,x 5,y 6,z 8,t 7,6543210,(12/12),(14/14),(7/16),(0/7),(0,5),(8,8),(0,10),图例:y还是有赢余.当当前弧移动到另局列表的尾部时,y再一次被重标号,并把赢余还给s.检查结束,y被提到L列表的首部.,S -20,x 5,y 0,z 8,t 7,6543210,(12/12),(8/14),(7/16),(0/7),(0,5),(8,8),(0,10),图例:检查x.注意x的当前弧指针已经指在t上了. x把赢余推给t. u指针直接后移.(因为x没

31、有被重标号),S -20,x 0,y 0,z 8,t 12,6543210,(12/12),(8/14),(12/16),(0/7),(0,5),(8,8),(0,10),图例:z被检查并被提到列表首部.,S -20,x 0,y 0,z 0,t 20,6543210,(12/12),(8/14),(12/16),(0/7),(0,5),(8,8),(8,10),图例:u指针从y开始向后移动,直到队尾也没有发现可以检查的结点(只有溢出的结点才能被检查).算法结束.,relabel-to-front的正确性,前面我们已经证明了一般预流推进算法的正确性了.因此,现在只要证明,在relabel-to-

32、front算法结束时,一般预流推进算法的结束条件也正好被满足-即没有溢出的结点.,结论11:L始终拓扑有序,对于G上的可行网络Gf,h,列表L中的结点始终保持拓扑有序性. 一开始的时候,列表中所有结点(s,t不在列表中)的高度均为0,不存在高度差,所以不存在可行弧.这时列表显然拓扑有序. 一个结点被relabel之后,就被提到列表的首部.根据辅助定理11,relabel之后没有可行弧进入结点,但有可行弧离开结点,所以将结点提到列表首部仍旧使列表满足拓扑有序. 推进操作不能创造可行弧,因此与列表的拓扑有序性无关.,结论12,L中指针u之前的结点全部是非溢出结点. 当一个结点被检查之后,它必定没有

33、赢余,因此将u指针后移不影响上面的性质. 它自己没有赢余了,但它却可能将赢余推给了别人.如果推给在L中位置在它后面的结点不要紧.但如果它把赢余推给了在自己之前的结点呢? 因为L拓扑有序,他若把赢余推给了排在自己前面的结点,则必定发生了relabel操作.而如果有relabel,则它已经被提到列表的首部了.性质依然满足. 这就是算法名:relabel-to-front的由来.,Relabel-to-front,根据一般的预流推进算法,当没有溢出结点时算法就结束并得到一个最大流. 而relabel-to-front算法的结束条件是u指针指向L队列尾部. 根据辅助定理12,u以前的结点均非溢出结点.

34、所以当u指向尾部时,所有的结点均没有溢出. 另外可以证明,算法的复杂度为O(n3).,Highest-relabel,还可以改进. 经验表明,总是检查高度标号最大的结点,会有比较好的效率. 于是对Relabel-to-front进行了一点小修改,得到了highest-relabel算法.,分块的L列表,可以证明,任意结点的最大的距离标号为2n-1. 将L列表分成2n个块,第1块保存所有高度为0的点,第i+1块保存高度为I的所有结点. 从最后一块开始往前找,发现一个不为空的块就把这个块里的结点全部检查掉.如果有元素被重标号了,那就将他移动到新的块里,并从那个新的块的前面开始继续往下查找.,分块的

35、L列表,对于可行网络,分块L列表是拓扑有序的.因为可行弧(u,v)要求h(u) = h(v) + 1,即只有从标号高的结点指向标号低的结点.既只有从后面的块里的结点指向前面的块里的结点.所以,这种分块方法仍然保持了整个列表的拓扑有序性.因此,算法结束时没有溢出的结点.因此该算法是正确的.,分块L列表的实现,如果用链表,可以只占用O(n)的空间。在内存不紧张的情况下,也完全可以用无序数组,时间效率不比链表差,虽然空间是O(n2)的. 无序数组在删除的时候,可以用: Delete(i) ai an Dec(n),更多的改进,回顾一下上面两个算法的正确性: 1).h是一个合法的高度函数 Gf不存在增

36、广路 2).同一结点h值保证递增 重标号后没有可行弧进入结点 列表L永远拓扑有序Relabel-to-front(highest-relabel)算法正确.也就是说,只要满足1).2).两条,h的值具体怎么变化并不重要.,更多的改进,可以发现,每当重标号操作被执行,该结点的当前弧的位置就被重置,同时这个结点被往前提,然后这个结点后面的结点又全部得被检查一遍.也就是说,每次重标号操作都很新产生很多工作,我们应该尽量减少重标号的次数. 而h值的具体变化规则不影响正确性.于是,我们希望h值能增长得快一点.(在满足h函数的合法性和单调递增性的情况下),BFS预处理,从前面的图例中可以发现,有些结点根本

37、没做什么事,第一件事就是重标号.我们希望可以将事先将他们的初始标号算好,从而减小重标号次数. 从汇点t开始做BFS,将经过的结点(s除外)的高度标号预设为该结点到t的最短路径. 可以证明,用这个预处理优化过的算法仍然是正确的.,如图所示的残量网络.因为我们希望h增长得尽量快,因此想象在图的底部有风在从低往高吹.,s,v1,v4,v3,t,6543210,v2,v5,但是直接吹是吹不动的.残量网络中的每一条边都是一层限制(从高处指向低处的边才是限制.从低到高的边暂时是没有限制作用的),牢牢地将结点捆住至多把v2和v3吹到高度为6的位置上.,s,v1,v4,v3,t,6543210,v2,v5,如

38、果v4向t作了一次饱和推进,导致(v4,t)消失,这时我们发现,v1,v5,v2,v3,v4都几乎属于完全自由的状态!,s,v1,v4,v3,t,6543210,v2,v5,v1,v5,v2,v3,v4都可以上升到高度6,而不影响h的合法性和递增性.重标号操作被大大减少.(注意,(t,v4)是从低到高的边,没有限制作用),s,v1,v4,v3,t,6543210,v2,v5,间隙优化:如果(0,s)之间的某个高度l不存在任何结点,那么处在(l,s)之间的所有结点都失去了原先的限制,可以让他们全部移到s+1的高度而不失正确性.,s,v1,v4,v3,t,6543210,v2,v5,间隙优化,间隙优化十分有用。在具体实现时有惊人的效果。 正好,在Highest-relabel算法中,我们按结点的高度分成了2n个块,只要顺便观察一下,如果有某个块为空了,那么立即应用间隙优化即可。 在某次Check操作结束后查看被检查的结点原先所在的块是否为空。 备注中为我的Highest-relabel算法的代码(带BFS预处理和间隙优化) (150行),效率测试(时间单位:s)(忽略了读数据的时间),

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 企业管理 > 管理学资料

本站链接:文库   一言   我酷   合作


客服QQ:2549714901微博号:道客多多官方知乎号:道客多多

经营许可证编号: 粤ICP备2021046453号世界地图

道客多多©版权所有2020-2025营业执照举报