网络流.docx
- 文档编号:8526143
- 上传时间:2023-01-31
- 格式:DOCX
- 页数:16
- 大小:97.65KB
网络流.docx
《网络流.docx》由会员分享,可在线阅读,更多相关《网络流.docx(16页珍藏版)》请在冰豆网上搜索。
网络流
第十六章网络流及其应用1
第一节网络流的基本概念和最大流算法1
第二节网络流例题解析4
第十六章网络流及其应用
近年来,很多信息学奥赛的题目都涉及到网络流算法,是高水平竞赛中考查的重要知识点,本章主要介绍网络流的基本概念、最大流的相关算法及应用实例。
第一节网络流的基本概念和最大流算法
一、网络流的基本概念。
1.流网络
如图16-1所示是联结某产品地v1和销售地v4的交通网,每一弧(vi,vj)代表从vi到vj的运输线,产品经这条弧由vi输送到vj,弧旁的数表示这条运输线的最大通过能力。
产品经过交通网从v1到v4。
现在要求制定一个运输方案使从v1到v4的产品数量最多。
图16-1流网络示意图
在有向图G(V,E)中,对于每条边(u,v)∈E,定义容量u(u,v)>=0为该边通过的流的上限(为了方便起见,假设对于每条边(u,v),在图中一定存在它的反向边(v,u)。
若(v,u)不存在,则添加这条边,容量为0,记f(u,v)为边(u,v)实际通过的流量。
(1)定义两个特殊点:
s,t,其中s表示源点(产生流的点),t表示汇点(收集流的点)
(2)对于普通的点,流入流量要等于流出流量。
2.增广路径
定义残留容量r(u,v)=u(u,v)-f(u,v),也就是这条边最多还能流多少。
定义G的残留网络G’为(V,E’),其中E’为E中所有残留容量大于零的边。
残留网络中任意一条从s到t的有向路径,称为增广路径。
3.最大流问题
最大流问题即是求,在满足下列限制时,从源点s到汇点t,最多能通过多大的流。
目标:
最大化f,f=∑f(s,i)=∑f(i,t)
容量限制:
对任意(u,v),f(u,v)<=u(u,v)
反对称性:
对任意(u,v),f(u,v)=-f(v,u)
流量守恒:
对任意u(u≠s或t),∑f(u,i)=∑f(i,u)=0(流入=流出)。
4.割
将所有的点分为两部分,则称为割。
连接属于不同部分点的边,称为割边。
例如图16-2中1-4-3-6就是一条路径,4-3-6-5-4是一条环,对于图16-2的割,(2,3)、(3,4)、(4,5)是割边。
图16-2割示意图
二、最大流算法
基本的想法:
尽量从s到t找到一条增广路径并增广,直到没有增广路径为止。
这个方法叫做Ford-Fulkerson方法。
具体实现,可以采用DFS或BFS实现。
但是对于朴素的实现,DFS效率较低。
采用BFS实现,则可以保证每次都沿着最短的一条增广路进行增广。
时间复杂度O(nm2)。
易于编程实现,对于规模较小的问题的首选算法。
具体实现过程:
1.找到增广轨后,求当前可以增大的流量。
proceduretrack_back;
vardelta:
longint;
begin
delta:
=maxlongint;
k:
=n;
whilepre[k]<>-1do
begin
delta:
=min(delta,c[pre[k],k]);
k:
=pre[k];
end;
inc(maxflow,delta);
k:
=n;
whilepre[k]<>-1do
begin
dec(c[pre[k],k],delta);
inc(c[k,pre[k]],delta);
k:
=pre[k];
end;
end;
2.深度优先搜索或宽度优先搜索寻找增广轨(时间复杂度O(V^2*E))。
【深度优先搜索的参考过程】
functionfind(k:
longint):
boolean;
vari:
integer;
begin
ifk=nthenexit(true);
fori:
=1tondo
ifc[k,i]>0then
begin
pre[i]:
=k;
iffind(i)thenexit(true);
end;
exit(false);
end;
3.最短增广路算法(SAP:
ShortestAugmentingPathAlgorithm)(时间复杂度O(V*E^2))。
基本思想是:
依次通过标号差为1的结点找增广轨,以保证找到的增广路是当前最短的。
如果标号出现断链,则需要进行调整来维持。
如果c[i,j]>0且d[i]-d[j]=1那么叫允许弧,先从汇点出发宽度遍历所有结点,求出所有结点到汇点所经过的得最少路径数,用宽搜实现。
procedureSAP;
varpre:
array[0..maxn+1]oflongint;
begin
i:
=1;
whiled[i] begin find: =false; forj: =1tondo ifc[i,j]>0then begin find: =true; pre[j]: =i; i: =j; break; end; iffindthen//找到允许弧,继续寻找 begin ifi=nthen begin track_back;//到达汇点,路径完整,求这条路上的最大流 i: =1; end; continue; end; d[i]: =maxint;//如果没找到允许弧,调整位与之相邻的最小的d[j]+1 forj: =1tondo if(c[i,j]>0)and(d[i]>d[j]+1)thend[i]: =d[j]+1; ifi<>1theni: =pre[i]; end; end; 4.SAP的间隙优化。 定义sum[k]为距离标号为k的结点数,在重标记过程中,当sum[k]减到0时,算法马上结束。 点i被重标记则其距离标号必然小于n。 而此时不存在此标号的顶点,则容许网络中必然不存在一条增广路径,因此算法结束。 这个优化使得算法效率显著提高。 第二节网络流例题解析 例16-1分配工作 【问题描述】 工厂有N个工人,一共需要做M种工作。 每个工人可以做一部分工作。 一项工作只能由一个人来做,而一个人最多只能做一项工作。 请你求出,最多有多少项工作可以被完成。 【输入】 第一行三个整数N,M,K。 接下来K行每行两个整数ai,bi,表示工人ai可以做工作bi。 【输出】 一个整数,表示可以完成的工作数量。 【输入样例】 333 11 21 33 【输出样例】 2 【分析】 若对于每个人与其可以加工的零件连一条权为一的边,之后从图外的一个源点对每个工人引一条边,从图外的一个汇点对每个工件引一条边。 求从源点到汇点的最大流即可求出工人与工件间最多同时存在的边数,亦即最多加工的工件数。 至此此题被转化为网络流问题,可以解决。 【参考程序】 var map,max: array[0..2001,0..2001]oflongint; q,p: array[0..2001]oflongint; v: array[0..2001]ofboolean; n,m,i,j,k,e,s,a,b,sum: longint; functionbfs(): boolean; var i,j,x: longint; begin q[1]: =0;s: =0;e: =1; fori: =1ton+m+1dov[i]: =false;v[0]: =true; repeat inc(s); x: =q[s]; ifx=n+m+1thenexit(true); fori: =1ton+m+1do if(max[x,i]-map[x,i]>0)and(v[i]=false)thenbegin inc(e);ife>2000thene: =1; q[e]: =i;p[e]: =s;v[i]: =true; end; ifs>2*(n+m)thenbreak; ifs>2000thens: =1; untils=e; exit(false); end; begin readln(n,m,k); fori: =1tokdobegin readln(a,b); b: =b+n; max[a,b]: =1; max[b,a]: =0; end; fori: =1tondobeginmax[0,i]: =1;max[i,0]: =0;end; fori: =1tomdobeginmax[n+i,n+m+1]: =1;max[n+m+1,n+i]: =0;end; whilebfsdobegin i: =s;inc(sum); repeat j: =i; i: =p[i]; inc(map[q[i],q[j]]); dec(map[q[j],q[i]]); untilp[i]=0; end; writeln(sum); end. 例16-2ditch 【题目来源: USACOTraining4.2DrainageDitches】 【问题描述】 在农夫约翰的农场上,每逢下雨,Bessie最喜欢的三叶草地就积聚了一潭水。 这意味着草地被水淹没了,并且小草要继续生长还要花相当长一段时间。 因此,农夫约翰修建了一套排水系统来使贝茜的草地免除被大水淹没的烦恼(不用担心,雨水会流向附近的一条小溪)。 作为一名一流的技师,农夫约翰已经在每条排水沟的一端安上了控制器,这样他可以控制流入排水沟的水流量。 农夫约翰知道每一条排水沟每分钟可以流过的水量,和排水系统的准确布局(起点为水潭而终点为小溪的一张网)。 需要注意的是,有些时候从一处到另一处不只有一条排水沟。 根据这些信息,计算从水潭排水到小溪的最大流量。 对于给出的每条排水沟,雨水只能沿着一个方向流动,注意可能会出现雨水环形流动的情形。 I【输入】(fileditch.in) 第1行: 两个用空格分开的整数N(0<=N<=200)和M(2<=M<=200)。 N是农夫John已经挖好的排水沟的数量,M是排水沟交叉点的数量。 交点1是水潭,交点M是小溪。 第二行到第N+1行: 每行有三个整数,Si,Ei,和Ci。 Si和Ei(1<=Si,Ei<=M)指明排水沟两端的交点,雨水从Si流向Ei。 Ci(0<=Ci<=10,000,000)是这条排水沟的最大容量。 【输出】(fileditch.out) 输出一个整数,即排水的最大流量。 【输入样例】 54 1240 1420 2420 2330 3410 【输出样例】 50 【分析】 这个题是典型的最大流问题,由于数据规模很小,我们可以使用BFS找增广路。 每次找增广路的时候把汇能增广的流量加入到最大流中,直到无法增广为止。 【参考程序】 programditch; const MXN=207; var n,m,s,t,i,u,v,w: longint; f,c: array[1..MXN,1..MXN]oflongint; pre,low,q: array[1..MXN]oflongint; vis: array[1..MXN]ofboolean; functionmaxflow: longint; var flow,l,r,u,v: longint; begin flow: =0; repeat fillchar(low,sizeof(low),0); fillchar(vis,sizeof(vis),0);vis[s]: =true; fillchar(q,sizeof(q),0);low[s]: =maxlongint; q[1]: =s;l: =1;r: =2; whilel inc(l);u: =q[l]; forv: =1tondo ifnotvis[v]andc[u,v]>f[u,v]thenbegin vis[v]: =true;pre[v]: =u; inc(r);q[r]: =v; low[v]: =c[u,v]-f[u,v]; iflow[v]>low[u]thenlow[v]: =low[u]; end; end; iflow[t]>0thenbegin u: =t; repeat f[pre[u],u]: =f[pre[u],u]+low[t]; f[u,pre[u]]: =f[u,pre[u]]-low[t]; u: =pre[u]; untilu=s; flow: =flow+low[t]; end; untillow[t]=0; exit(flow); end; begin assign(input,"ditch.in"); assign(output,"ditch.out"); reset(input);rewrite(output); read(m,n); s: =1;t: =n; fori: =1tomdobegin read(u,v,w); c[u,v]: =c[u,v]+w; end; writeln(maxflow); close(input);close(output); end. 例16-3K-联赛 【问题描述】 K-联赛职业足球俱乐部的球迷们都是有组织的训练有素的啦啦队员,就像红魔啦啦队一样(2002年韩日世界杯上韩国队的啦啦队)。 这个赛季,经过很多场比赛以后,球迷们希望知道他们支持的球队是否还有机会赢得最后的联赛冠军。 换句话说,球队是否可以通过某种特定的比赛结果最终取得最高的积分(获胜场次最多)。 (允许出现多支队并列第一的情况。 ) 现在,给出每个队的胜负场数,wi和di,分别表示teami的胜场和负场(1≤i≤n)。 还给出ai,j,表示teami和teamj之间还剩多少场比赛要进行(1≤i,j≤n)。 这里,n表示参加联赛的队数,所有的队分别用1,2,…,n来编号。 你的任务是找出所有还有可能获得冠军的球队。 所有队参加的比赛数是相同的,并且为了简化问题,你可以认为不存在平局(比赛结果只有胜或负两种)。 【输入】 第一行一个整数n(1≤n≤25),表示联赛中的队数。 第二行2n个数,w1,d1,w2,d2,……,wn,dn,所有的数不超过100。 第3到n+2行每行n个数,第i+2行的n个数为ai,1,ai,2,…,ai,n。 所有的数都不超过10。 ai,j=aj,i,如果i=j,则ai,j=0。 【输出】 若干行,输出所有可能获得冠军的球队,按其编号升序输出,每行一个。 【输入样例】 3 201102 022 202 220 【输出样例】 123 【分析】 对于队伍i,如果存在时期总分最高的方案,那么显然至少存在一种队伍i剩下比赛全胜而最终总分最高的方案,因此先使得i的剩余比赛均获胜,之后因为其他队伍得分不能超过i,而每场比赛都必须决出胜负。 对此,可以构图,对于a,b的一场比赛,从a向a`,b`各引一条容量为一的边,从一个源向所有a引容量无限的边,之后根据各队不同情况确定各a`到汇的边的容量,求出最大流即是最多可以决出胜负的场数,显然地,只有当此场数等于比赛总数时,i才可以夺冠。 【参考程序】 var map,m: array[0..50,0..51]oflongint; a: array[1..25,1..25]oflongint; w: array[1..25]oflongint; v: array[0..51]ofboolean; q,pre,t: array[0..52]oflongint; n,i,j,k,l,r,x,h,sum,max,flow: longint; functionmin(a,b: longint): longint; begin ifa elseexit(b); end; begin readln(n); fori: =1tondoread(w[i],x); fori: =1tondo forj: =1tondobegin read(a[i,j]); ifi inc(sum,a[i,j]); inc(map[i,j+n],a[i,j]); inc(map[0,i],a[i,j]); end; end; t[0]: =maxlongint;pre[0]: =-1;h: =n*2+1; fork: =1tondobegin m: =map;max: =w[k]; fori: =1tondobegin inc(max,a[i,k]); ifi end; m[0,k]: =0; fori: =1tondo m[i+n,h]: =max-a[i,k]; flow: =0; repeat q[1]: =0;l: =0;r: =1; fillchar(v,sizeof(v),false); repeat inc(l); fori: =1tohdo if(notv[i])and(m[q[l],i]>0)thenbegin pre[i]: =q[l];t[i]: =min(t[q[l]],m[q[l],i]); v[i]: =true;inc(r);q[r]: =i; end; ifv[h]thenbreak; untill=r; ifv[h]thenbegin r: =h;inc(flow,t[h]); whilepre[r]<>-1dobegin dec(m[pre[r],r],t[h]); inc(m[r,pre[r]],t[h]); r: =pre[r]; end; end; untilnot(v[h]); ifsum-max+w[k]=flowthenwriteln(k); end; end. 例16-4破坏石油运输系统问题 【题目来源: RQNOJP306】 【问题描述】 某组织打算摧毁发烂稀国的石油运输系统。 该系统可以看成一个运输网络,由许多结点和连接结点的管道组成。 只有地点A生产石油,生产的石油通过管道运输到地点B,石油不能在中间点累积。 管道是双向的,每条管道连接两个不同的结点,每两个结点之间只有一条管道连接。 每条管道有一个抗压指数,当石油的流量超过这个数管道就会爆炸。 A地生产石油的速度是很快的,但由于抗压指数的问题,能运到B的有一个上限。 发烂稀国比较贪婪,他们采用了使他们获得最多石油的运输方案。 某组织有一个特殊的物质,能使一条管道的抗压指数下降1。 作为该组织的首席程序员,你的任务就是告诉领导,让那些管道的抗压指数下降,一定可以摧毁发烂稀国的石油运输网络。 【输入】 第1行包含四个整数n,m,s,t,表示有n个结点(编号为1,2,3,……,n),m条管道,s和t分别是A地和B地的编号。 2<=n<=130,0<=m<=n(n-1)/2,1<=s,t<=n。 接下来m行,每行描述一条管道,包含3个整数i,j,c。 i,j分别为管道连接的2个结点。 C为该条管道的抗压指数。 1<=i,j<=n,1<=c<=10000。 【输出】 第1行输出抗压指数减少1就爆炸的管道的条数k。 接下来k行每行输出一个整数p(1<=p<=m),说明第p条管道如果抗压指数减少1就必定爆炸。 序号p按照管道输入的顺序,并按照p的升序输出。 【输入样例】 4414 24100 121 31100 431 【输出样例】 2 2 4 【分析】 这道题目不难看出是一道网络流的题目,我们可以先求出当前情况下的最大流,然后对所有满流边进行减1,然后再求最大流,如果当前最大流小于先前最大流,这条边一定不是必要边,反之一定是一条必要边。 【参考程序】 programrqnoj306; constMXN=140; var g,c,f: array[1..MXN,1..MXN]oflongint; d,vd: array[0..MXN]oflongint; dx,dy,a,db: array[1..20000]oflongint; i,j,n,m,ans,tmp,s,t,x,y,v,useedge: longint; functionmin(a,b: longint): longint; begin ifa>bthenexit(b)elseexit(a); end; functiondfs(u,flow: longint): longint; var v,tmp,ans: longint; begin if(u=t)thenexit(flow); ans: =0; forv: =1tondoif(g[u][v]>0)and(d[u]-1=d[v])then begin tmp: =dfs(v,min(flow-ans,g[u][v])); dec(g[u][v],tmp);inc(g[v][u],tmp); if(c[u][v]>0)theninc(f[u][v],tmp)elseinc(f[v][u],tmp); inc(ans,tmp); if(ans=flow)thenexit(ans); end; if(d[s]>=n)thenexit(ans); dec(vd[d[u]]); if(vd[d[u]]=0)thend[s]: =n; inc(d[u]);inc(vd[d[u]]);exit(ans); end; functionsap(): longint; var ans,i,j: longint; begin ans: =0;vd[0]: =n; fori: =1ton-1dovd[i]: =0; fori: =1tondod[i]: =0; fori: =1tondo forj: =1tondo begin g[i][j]: =c[i][j];f[i][j]: =0; end; while(d[s] inc(ans,dfs(s,maxlongint)); exit(ans); end; begin readln(n,m,s,t); fori: =1tomdo begin readln(x,y,v); dx[i]: =x;dy[i]: =y;inc(c[x][y],v);inc(c[y][x],v); end; ans: =sap();j: =0;useedge: =0; fori: =1tomdoif((f[dy[i]][dx[i]]=c[dy[i]][dx[
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 网络