网络流基础及应用.docx
- 文档编号:8316159
- 上传时间:2023-01-30
- 格式:DOCX
- 页数:20
- 大小:137.25KB
网络流基础及应用.docx
《网络流基础及应用.docx》由会员分享,可在线阅读,更多相关《网络流基础及应用.docx(20页珍藏版)》请在冰豆网上搜索。
网络流基础及应用
网络流基础及应用
一.引例运输方案
下图为联结产品产地V1和销地V6的交通网,每一边(Vi,Vj)代表从Vi到Vj的运输线,产品经这条边由Vi输送到Vj,边旁的数字表示这条运输线的最大通行能力(简称容量)。
产品经过交通网从V1输送到V6,现要求制定一个输送方案,使V1运到V6的产品数量最多。
下面是一个可行的输送方案,边旁的数字为该运输线的实际运输量(单位:
吨)。
该运输方案表示:
2吨产品沿有向路P1(V1,V2,V4,V6)运到销地;1吨产品沿有向路P2(V1,V2,V5,V6)运到销地;2吨产品沿有向路P3(V1,V3,V5,V6)运到销地。
总共有5吨从V1运到V6。
运输方案的可行必须满足以下三个条件:
⑴实际运输量不能是负的;
⑵每条边的实际运输量不能大于该边的容量;
⑶除了起点V1和终点V6,对其他顶点(中间点)来说,不能囤积物资,即运到它那儿的物资是多少,从它那儿运走的物资也应该是多少。
运输方案的改进:
根据这个运输网,是否可增大运输量?
改进1:
我们找到一条有向路P4(V1,V2,V3,V4,V6)可再增加1吨物资运输量,改进的方案如下:
改进2:
有向路P5(V1,V3,V4,V6)还可增加1吨运输量:
改进3:
观察有向路P6(V1,V3,V2,V4,V6)中,将正方向的边(V1,V3)、(V2,V4)、(V4,V6)都可增加运输量,而反方向的边(V3,V2)的运输量为1,现将反向边(V3,V2)的运量调到正向边(V2,V4)上去完成,这样有向路P6(V1,V3,V2,V4,V6)的运量可增加1(想一想为什么可以这样做)。
至此,再找不到可“改进”的有向路了,所以,该交通网的最大运输量为8吨。
通过上述实例分析,包含了流量因素的问题,是一类特殊的图。
引例给出的交通网其具体的物理模型是多种多样的。
比如网络的有向边可以表示为城市之间的公路、电信网之间的通讯线路、天然气站之间的输气管道等;边的容量则可以表示为允许通过的物资数量、汽车数量、速率或最大信号流量等。
这一类特殊的图,称为网络流图。
网络流图中需要解决的基本问题是求最大流问题。
二.网络流图与最大流
类似于上述交通网,可构造下列称为网络流图的模型。
1、网络流图的构造
设G=(V,E)为一简单有向图。
当且仅当只有一个入度为0(d(z)=0)的点,在V中指定该顶点为源点(记为Z),当且仅当有一个顶点出度为0的顶点,为汇点(记为Z’),对于每一条边(Vi,Vj)∈E,赋予一个非负数Cij>=0,叫做该边的容量。
记为D=(V,E,C)(图示参见引例)
2、网络的流
对于网络流图D=(V,E,C)的每一条边(Vi,Vj)给定一个非负数fij,且满足下列条件:
①0<=fij<=Cij(容量限止条件)
②除源点和汇点外,其余顶点Vi恒有
∑fij=∑fki(中间点流量守恒,即平衡条件)
这时D=(V,E,C)中的流称为D的可行流f。
3、关于D的可行流
①当所有fij=0,称为零流,零流一定是可行流。
②对于源Z和汇Z’有
∑fZj=∑fjZ’=ω
数ω叫做网络流的流量。
③当fij=Cij时,称边(Vi,Vj)饱和,表示流f对于该边饱和。
④ω达到最大值时的流f,称为D=(V,E,C)的最大流。
三.求最大流的理论基础
1、割切概念——某些边的集合
⑴设D=(V,E,C)是已知的网络流图,假定S是V的一个子集,且S满足
①Z∈S②Z’(not∈)S
且令S’为S的补集,这样把顶点集V分成S和S’两个部分,其中
Z∈S,Z’∈S’
对于一个端点在S,而另一个端点在S’的所有边的集合,叫做网络流图D的一个割切,用(S,S’)表示。
下图用虚线划去的表示一个割切,其中S={Z,b,c,d},S’={a,Z’}。
<图片>
⑵割切容量C(S,S’):
在割切(S,S’)中,把从S到S’的边容量和叫做这割切的容量。
上边割切容量C(S,S’)=CZa+Cba+CbZ’+CdZ’=4+3+2+4=13
2、定理:
对于已知的网络流,从源点Z到汇点Z’的流量ω的最大值小于等于任何一个割切的容量,即Maxω<=MinC(S,S’)。
证明:
当Vi为网络流图的任一中间点时,根据平衡条件,恒有
∑fij-∑fji=0⑴
当i=Z时,∑fZj=ω⑵
设(S,S’)为一任一个切,则有
∑(fij-fji)=ω
注:
∵i∈S,Z’(not∈)S,Z∈S
当i≠Z时,由⑴式得0
当i=Z时,由⑵式得ω
又因为S∪S’=V
所以∑(fij-fji)=∑(fij-fji)+∑(fij-fji)=ω⑶
⑶式中∑(fij-fji)的两个下标都是对S的全体求和,其展开式中任意一项fpq都一一对应一项-fpq,所以∑(fij-fji)=0
即由⑶式得∑(fij-fji)=ω⑷
再由于0<=fij 由⑷式ω=∑(fij-fji)<=∑Cij=C(S,S’) 故有Maxω<=MinC(S,S’) 3、最大流量最小割切定理(Ford-Fulkerson定理) 在一个给定的网络流图上,流的极大值等于割切容量的最小值,即 Maxω=MinC(S,S’) (下面的证明是构造性的,可以从中引出求最大流的算法思想) 证明: (求证思路: 由于已知Maxω<=MinC(S,S’)是不能进一步增加流量,直到使Maxω=MinC(S,S’)) 在网络流图D=(V,E,C)中,定义从源点Z到汇点Z’的道路: 设Z=V0,V1,...,Vn-1,Vn=Z’为D上的顶点序列,对于i=0,1,...,n-1,恒有(Vi,Vi+1)或(Vi+1,Vi)边是D的一条边。 则称V0,V1,...,Vn是一条从Z到Z’的道路。 由于D是有向图,道路上边的方向与道路方向一致的边称为前向边P+,反之称为后向边P-。 道路上边的饱和: 前向边若fij=Cij,后向边若fij=0,称边(Vi,Vj)为关于该道路上的饱和边。 若从Z到Z’的道路上所有的边均不饱和,即对于P+若fij 修改可增广道路上每条边的流量,同时保持网络流的可行性,达到流量的增加,其增量的确定方法如下: Cij-fijP+ fijP- 令δij={ 取δ=Min(δij),为增量。 就是对可增广道路P的每一条前向边流量增加δ,每一条后向边流量减少δ,从而使得整个网络流的流量获得增加(这是显而易见的) 下面论证Maxω=MinC(S,S’) 设网络流图D的流量f达到极大,我们构造一个割切(S,S’)如下 若Z’∈S,则有一条从Z到Z’的道路,Z=V0,V1,...,Vk=Z’,这条道路上所有的边均不饱和,因而是条可增广道路,这和f是最大流的假设矛盾。 ①Z∈S ②若x∈S,且fxy 若x∈S,且fyx>0,则y∈S 显然Z’∈S’(Z’的出度d(Z’)=0), 所以(S,S’)是一个割切。 按照子集S的定义,若x∈S,y∈S’,则fxy=Cxy 若y∈S’,x∈S,则fyx=0 所以ω=∑(fxy-fyx)=∑fxy=∑C(S,S’) 即Maxω=MinC(S,S’) 通过以上论证,可知 ①当D中找不到可增广道路时,此时的流为最大流; ②当D中的流最大时,一定存在容量最小的割切(S,S’) 四.求网络最大流的算法 根据上述最大流最小割切定理及其论证,我们不难得出下面重要结论: 设f是网络流图D的一个流,如果存在从源点Z到汇点Z’的关于f可增广道路P,那么f一定是最大流。 至此,求最大流的基本思路为: ㈠标号法(Ford-Fulkerson算法) 标号法分为两个过程,一是标号过程,通过标号过程找到一条可增广道路或无法找到可增广道路;二是增广过程,沿可增广道路增加网络流量。 从一个可行流出发(若网络中没有初始流,则可以设f是零流) 1、标号过程: 用Vs表示源点,Vt表示汇点。 在此过程中,网络中的顶点或者是已标号点(又分为已检查和未检查两种),或者是未标号点。 每个标号点的标号包含两部分。 第一个标号指明它的标号是从哪个顶点得到的,以便找出可改进量,第二个标号是为确定可改进量δ而设的。 标号过程开始,先给源点Vs标上(0,+∞),这时Vs是已标号而未检查的点,其余都是未标号点。 一般地,取一个已标号而未检查的点Vi,对一切未标号点Vj: ⑴若存在弧(Vi,Vj)且fij ⑵若存在弧(Vj,Vi)且fji>0,则给Vj标号(-Vi,L(Vj)),其中L(Vj)=Min(L(Vi),fji), 至此,Vj成为已标号而未检查点。 在Vi的全部相邻顶点都已标号后,Vi成为标号而已检查的顶点。 重复上述步骤,一旦Vt被标号,表明得到一条从Vs到Vt的可增广道路P,转入增广过程。 若所有标号点都已检查过使得标号过程无法继续时,则算法结束,这是的可行流即为最大流。 2、增广过程 所得可增广道路P上的顶点都已标号。 因此多用“倒向追踪”的方法,从Vt开始,利用标号点的第一个标号可得P上一条边,并以Vt的第二个标号l(Vt)(即可增广流量δ)作为改进量,增大P上的流量。 具体步骤如下: ①取Vt的第一个标号Vk(或-Vk),则弧(Vk,Vt)(或相应的弧(Vt,Vk))是P上的弧,接下来再根据Vk的第一个标号Vi(或-Vi),再找到P上的弧(Vi,Vk)(或(Vk,Vi))……,直到找到Vs为止,这是所有被找出的弧就构成了可增广道路P。 根据第一个标号的正负号,可知其方向是前向还是后向。 令改进量δ=L(Vt),对弧上的流量fij作改进: fij+δ(Vi,Vj)为p+ fij-δ(Vi,Vj)为p- Fij’= ②去掉所有的标号,对新的流F’=(fij’)重新进入标号过程。 求最大流标号发的描述 ⑴数据结构 Constmaxn=<网络顶点数> Typenode=record{可增广道路的顶点类型} l,p: integer;{第一标号,检查标号} end; arc=record{网络边类型} c,f: integer;{容量,流量} end; gtype=array[0..maxn,0..maxn]ofarc;{网矩阵} ltype=array[0..maxn]ofnode;{可增广道路} varlt: ltype;{可增广道路} g: gtype;{网络} n,s,t: integer;{顶点数,源点,汇点} ⑵主要算法 1初始化网络,可增广道路 procedureread-graph; varI,j: integer; begin readln(n);{顶点数} fillchar(g,sizeof(g),0);{初始化网络} fillchar(lt,sizeof(lt),0);{初始化可增广道路} fori: =1tondo forj: =1tondo read(g[I,j].c); end; 2寻找已标号而来检查的顶点序号 functioncheck: integer; vari: integer; begin i: =1; while(i<=n)andnot((lt[i].l<>0)and(lt[i].p=0))doinc(i); ifi>nthencheck: =0{顶点不存在} elsecheck: =i; end; 3标号过程,并返回是否找到可增广道路及改进量a functionford(vara: integer): boolean;{无增广道路返回true} varI,j,m,x: integer; begin ford: =true; fillchar(lt,sizeof(lt),0);{去掉原来的标号} lt[s].l: =s;{从Vs开始} repeat i: =check;{寻找一个已标号而未检查的顶点i} ifi=0thenexit;{若该顶点不存在,则退出过程,返回true} forj: =1tondo if(lt[j].l=0)and((g[I,j].c<>0)or(g[j,i].c<>0) {寻找与Vi相邻且未标号的顶点j} thenbegin if(g[I,j].f =I; if(g[j,i].f>0)thenlt[j].l: =-I; end; lt[i].p: =1;{顶点Vi置已检查标志} until(lt[t].l<>0){直到汇点Vt标号为止} m: =t;a: =maxint;{从Vt倒推,改进量a赋初值} repeat{求a} j: =m;m: =abs(lt[t].l); iflt[j].l<0thenx: =g[j,m].f; iflt[j].l>0thenx: =g[m,j].c-g[m,j].f; ifa>xthena: =x; untilm=s;{直至例推到顶点Vs为止} ford: =false;{返回可增广道路存在标志false} end; 4修正流量 procedurefulkerson(a: integer); varm,j: integer; begin m: =t;{以顶点Vt出发,逆向沿可增广道路修正容量} repeat j: =m;m: =abs(lt[j].l); iflt[j].l<0theng[j,m].f: =g[j,m].f-a;{后向边p-} iflt[j].l>0theng[m,j].f: =g[m,j].f+a;{前向边p+} untilm=s; end; 5输出所有弧的流量 procedureanswer; varI,j: integer; begin fori: =1tondo forj: =1tondo writeln(‘(’,I,‘,’,j,‘)’,‘’,g[I,j].f) end; 6算法合成 procedureproceed;{求最大流} vard: integer; success: boolean; begin s: =1;t: =n;{假设源点为V1,汇点为Vn} repeat success: =ford(d);{寻找可增广道路及改进量d} ifsuccessthenanswer{增广道路不存在,输出最大流} elsefulkerson(d){沿增广道路修正流量} untilsuccess; end; 7主程序 begin read_graph;{输入网} proceed;{求最大流} end. ㈡计算最大流的dinic算法 Dinic算法的思想是分阶段地在层次图中改进流量。 下面先介绍网络图的剩余图及层次图概念。 剩余图: 给定一个网络流图D1=(V1,E1,c)及一可行流f,与该网络流图D1=(V1,E1,c)关于流f的剩余图D2=(V2,E2),D2的顶点集与D1的顶点集相同,即V2=V1。 对于D1中的任一条有向边(u,v)∈E1,若fuv 若fuv>0,那么边(u,v)∈E2,…………guv=fuv,显然改变为后向边。 由此可见,网络流图D1中的每条边在剩余图D2中都化作了一条或两条边,D2中的每条边都表示在D1中能沿某方向则增广,D2中边(u,v)的权值guv表示在D1中能够沿着Vu到Vv的方向增广大小为guv的流量。 见下图。 层次图: 在剩余图中,把源点到点i的最短路径长度称作点i的层次,记为level(i)。 源点s的层次为0。 层次图的构造是这样的,设层次图D3=(V3,E3),对于剩余图D2=(V2,E2)中的一条边(u,v),当且仅当level(v)=level(u)+1时,边(u,v)∈E3,V3={u∣E3中有边与u相连}。 直观地讲,层次图是建立在剩余图基础上的一张“最短路径图”。 从源点开始,在层次图中沿着边不管怎么走,经过的路径一定是终点在剩余图中的最短路径。 在D2中,从源点vs到汇点vt的任意一条简单路径(即不存在重复顶点或边的路径)都对应可增广路径,路径上每条边的权值的最小值即为能够一次增广的容量。 1.dinic算法的基本流程 算法是循环结构,将每一次循环称为一个阶段,在每个阶段中,首先根据剩余图建立层次图,然后用dfs过程在层次图内扩展可增广路径,调整流量。 增广完毕后,进入下一个阶段。 这样不断重复,直到汇点不再层次图内出现为止,汇点不再层次图内意味着在剩余图中不存在从源点到汇点的路径,即没有可增广路径。 在算法实现中,层次图并不是构建出来的只需在剩余图中对每个顶点标记层次level,增广时,判断边是否满足level(u)+1=level(v)约束即可。 2、Dinic算法描述 (1)数据结构 Constmaxn=<顶点数上限> maxm=<边数上限> maxw=+∞{取一个极大数} type gtype=record(边类型) x,y,c,f,next,op: longint;{分别为边(x,y),容量,流量,后继指针,反向指针} end; Var g: array[1..maxm*2]ofgtype;{以边目表存储网络流图} first,first1: array[1..maxn]oflongint;{顶点Vi引出的首条边序号first[i]} p,level,prt: array[1..maxn]oflongint;{队列或栈p,顶点Vi的层次level,可增广路 径上顶点Vi引出的边序号prt[i]} visited: array[1..maxn]oflongint;{访问序列} n,m,tot,vs,vt,maxflow,temp: longint;{顶点数n,边数m,边序号tot,最大流量maxflow} ⑵构造初始剩余图。 将网络图的边目表,及初始流f=0,通过add(a,b,c)过程插入容量为c的边(a,b)和容量为0的反向边(b,a)。 边目表的存储的链表形式: 将顶点Vi引出的所有边存储在一个链表中,首条边的序号为first[i],顺着next指针,依次访问顶点Vi引出的所有边。 过程add。 Procedureadd(a,b,c: longint); Begin Inc(tot);g[tot].x: =a;g[tot].y: =b;g[tot].c: =0; g[tot].next: =first1[a];first1[a]: =tot;{在a顶点引出的边表尾部插入该条边} Iffirst[a]=-1thenfirst[a]: =tot; g[tot].op: =tot+1;{设置反向边指针} inc(tot);g[tot].x: =b;g[tot].y: =a;g[tot].c: =0;{新增一条容量为0的边(b,a)} g[tot].next: =first1[b];first1[b]: =tot;{在b顶点引出的边表尾部插入该条边} Iffirst[b]=-1thenfirst[b]: =tot; g[tot].op: =tot-1;{设置反向边指针} end; 构建初始剩余图,设f=0为零流。 Fillchar(g,sizeof(g),0);{网络流图初始化为空} Readln(n,m);{读入顶点数,边数} Fori: =1tondobegin{每个顶点引出的边集初始化为空} first[i]: =-1; first1: =-1 end; Tot: =0;{边序号初始化} Fori: =1tomdo{读入第i条边(a,b)及容量c,将该边和反向边插入到g中} Begin Readln(a,b,c); Add(a,b,c); End; ⑶通过宽度优先搜索计算顶点层次level。 首先源点进队列,然后按照“层次”搜索剩余图,取出队首元素,搜索剩余图中队首元素引出的所有边。 若边的两端点的层次至少相差2层以上,则另一个端点入队,该端点置于队首元素的下一层。 依此类推,直至队列空。 Proceduremake_level; VarI,open,closed,temp,tp: longint;{队首指针open,队尾指针closed,队首元素tp} Begin Fori: =1tondolevel[i]: =maxw;(每个顶点层次初始化) Fillchar(p,sizeof(p),0);{队列置空} Open: =1;closed: =0;p[open]: =vs;level[vs]: =1;{源点vs进入队列,层次为1} Whileclosed Begin Inc(closed);tp: =p[closed]; Iftp=vtthenexit;{若队首为汇点,则退出} Temp: =first[tp];{搜索剩余图中与队首顶点相连的所有边} Whiletemp<>-1do Begin Iflevel[g[temp].y]>level[tp]+1 Thenif(g[temp].f Then {若当前边属于剩余图,则另一端进入队列,其层次为队首顶点的下一层} Begin Inc(open);p[open]: =g[temp].y;level[g[temp].y]: =level[tp]+1; End; Temp: =g[temp].next;{取队首顶点相连的下一条边} End; End; End; 执行了make_level过程后,来计算出汇点Vt的层次,(level[Vt]=maxw),则说明源点Vs与汇点Vt间无路可通,不存在可增广路径,当前流为最大流。 在计算出层次图后,则通过dfs扩展可增广路径来调整流量。 ⑷在层次图内扩展可增广路径,调整流量。 使用堆栈存储目前找到的可增广路径,栈顶指针指向路径中的最后一个顶点。 一开始,栈中只有源点。 Dfs过程分两个操作: ①如果栈顶元素为汇点,即找到了可增广路径,那么对栈中的可增广路径进行流量调整。 流量调整后,栈顶里饱和边对应顶点间的所有元素出栈,以后进需要对栈中余下路径进行增广; ②如果栈顶元素u非汇点,且栈顶元素在层次图中连出的属于剩余图的边(u,v),则顶点v入栈,可增广路径中又扩展出一条边(u,v),并继续以u引出的下一条边出发进行dfs遍历;若u在
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 网络 基础 应用
![提示](https://static.bdocx.com/images/bang_tan.gif)