导航:首页 > 源码编译 > 最大流算法代码

最大流算法代码

发布时间:2024-02-04 19:57:05

‘壹’ 高分:网络流问题

一、引言

网络流算法是一种高效实用的算法,相对于其它图论算法来说,它的模型更加复杂,编程复杂度也更高。但是它综合了图论中的其它一些算法(如最短路径、宽度搜索算法),因而适用范围也更广,经常能够很好地解决一些搜索与动态规划无法解决的非np问题。
网络流在具体问题中的应用,最具挑战性的部分是模型的构造,它没用现成的模式可以套用,需要我们对各种网络流的性质了如指掌(比如点有容量、容量有上下限、多重边等等),根据具体的问题发挥我们的创造性。一道问题经常可以建立多种模型,不同的模型对问题的解决效率的影响也是不同的,本文通过实例探讨如何确定适当的模型,提高网络流算法的效率。

二、网络流算法时间效率

当我们确定问题可以使用最大流算法求解后,就根据常用的ford-fulkerson标号法求解;而最小(大)费用最大流问题也可用类似标号法的对偶算法解题。ford-fulkerson标号法的运行时间为o(ve2),对偶法求最小费用流的运行时间大约为o(v3e2)。

显然,影响网络流算法的时间效率的因素主要是网络中顶点的数目与边的数目。这二个因素之间不是相互独立的,而是相互联系,矛盾而统一的。在构造网络模型中,有时,实现了某个因素的优化,另外一个因素也随之得到了优化;有时,实现某个因素的优化却要以增大另一因素为代价。因此,我们在具体问题的解决中,要坚持"全局观",实现二者的平衡。

三、模型的优化与选择

(一)减少模型的顶点数与边数,优化模型

如果能根据问题的一些特殊性质,减少网络模型中的顶点的数目和边的数目,则可以大大提高算法的效率。

例1:最少皇后控制

在国际象棋中,皇后能向八个方向攻击(如图1(a)所示,图中黑点格子为皇后的位置,标有k的格子为皇后可攻击到的格子)。现在给定一个m*n(n、m均不大于于50)的棋盘,棋盘上某些格子有障碍。每个皇后被放置在无障碍的格子中,它就控制了这个格子,除此,它可以从它能攻击到的最多8个格子中选一个格子来控制,如图1(b)所示,标号为1的格子被一个皇后所控制。

请你编一程序,计算出至少有多少个皇后才能完全控制整个棋盘。

图1(a) 图1(b)

输入格式:
输入文件的第一行有两个整数m和n,表示棋盘的行数与列数。接下来m行n列为一个字符矩阵,用''.''号表示空白的格子,''x''表示有障碍的格子。

输出格式:
输出文件的第一行仅有一个数s,表示需要皇后的数目。
sample input
3 4
x...
x.x.
.x..
sample ouput
5

问题分析]

如果本问题用简单的搜索来做,由于题目给的棋盘很大,搜索算法很难在短时间内出解。由于一个皇后在棋盘最多只能控制两个格子,因此最少需要的皇后数目的下界为[n*m/2]。要使得皇后数目最少,必定是尽量多的皇后控制两个格子。如果我们在每两个能相互攻击到的格子之间加上一条有向弧,则问题很类似于二分图的最大匹配问题。

[模型一]

1. 将每个非障碍的格子按行优先编号(0~m*n-1)。
2. 将上述的每个格子i折成两个格子i''和i'''',作为网络模型中的顶点。
3. 若格子i可以攻击到格子j且i<j,则在模型中顶点i''到j''''之间加上一条有向弧,容量为1。
4. 增加一个源点s,从s点向所有顶点i''添上一条弧;增加一个汇点t,从所有顶点j''''到t添上一条弧,容量均为1。

图1(b)所示的棋盘,对应的模型为:

图2

显然,任一解对应于以上模型的一个最大匹配。且最大匹配中,匹配数必定是偶数。因此至少需要的马匹数为m*n-障碍数-最大匹配数/2。

[模型二]

如果我们将棋盘涂成黑白相间的格子,则某皇后控制的两个格子一定是一个是黑格,另一个是白格(如图3),不妨设这两个格子中皇后在白格子上。于是,我们将n*m个格子分成两部分白格与黑格。因此我们可以将模型一优化为:

图3

1.将棋盘中的所有格子分成两个部分,对所有的格子进行编号,每个白格与它能攻击到的黑格之间(障碍除外)添上一条从白格到黑格的弧,构成一个二分图。

2.增加一个源点s,从s点向所有非障碍的白格添上一条弧;增加一个汇点t,从所有非障碍的黑格到t添上一条弧。

3.设置所有的弧的流量为1。
图1(b)所示的棋盘,对应的模型为:

图4

[两种模型的比较]

显然,模型二的顶点数与边数大致是模型一的一半。下面是在bp环境下两种模型的时间效率比较(p166/32m):

模型一 模型二

可扩展性 不易打印出一种解 容易打印出一种解

模型二正是根据问题的特殊性(即马的走法),将网格中的格点分成白与黑两类,且规定马只能从白格跳到黑格,从而避免将每个格点折分成两个点,减少模型的顶点数,同时也大大减少了边的数目。达到了很好的优化效果。

(二)综合各种模型的优点,智能选择模型

有时,同一问题的各种模型各有特色,各有利弊。这种情况下,我们就要综合考虑各种模型的优缺点,根据测试数据智能地选择问题的模型。

例2火星探测器(ioi97)

有一个登陆舱(pod),里边装有许多障碍物探测车(mev),将在火星表面着陆。着陆后,探测车离开登陆舱向相距不远的先期到达的传送器(transmitter)移动,mev一边移动,一边采集岩石(rock)标品,岩石由第一个访问到它的mev所采集,每块岩石只能被采集一次。但是这之后,其他mev可以从该处通过。探测车mev不能通过有障碍的地面。
本题限定探测车mev只能沿着格子向南或向东从登陆处向传送器transmitter移动,允许多个探测车mev在同一时间占据同一位置。

任务:计算出所有探测车的移动途径,使其送到传送器的岩石标本的数量最多,且使得所有的探测车都必须到达传送器。

输入:

火星表面上的登陆舱pod和传送器之间的位置用网络p和q表示,登陆舱pod的位置为(1,1)点,传送器的位置在(p,q)点。

火星上的不同表面用三种不同的数字符号来表示:

0代表平坦无障碍
1代表障碍
2代表石块。
输入文件的组成如下:
numberofvehicles
p
q
(x1y1)(x2y1)(x3,y1)…(xp-1y1)(xpy1)
(x1y2)(x2y2)(x3,y2)…(xp-1y1)(xpy2)
(x1y3)(x2y3)(x3,y3)…(xp-1y3)(xpy3)

(x1yq-1)(x2yq-1)(x3,yq-1)…(xp-1yq-1)(xpyq-1)
(x1yq)(x2yq)(x3,yq)…(xp-1yq)(xpyq)
p和q是网络的大小;numberofvehicles是小于1000的整数,表示由登陆舱pod所开出的探测车的个数。共有q行数据,每行表示火星表面的一组数据,p和q都不超过128。

[模型一]

很自然我们以登陆舱的位置为源点,传送器的位置为汇点。同时某块岩石由第一个访问到它的mev所采集,每块岩石只能被采集一次。但是这之后,其他mev可以从该处通过,且允许多个探测车mev在同一时间占据同一位置。因此我们将地图中的每个点分成两个点,即(x,y)à(x,y,0)和(x,y,1)。具体的描述一个火星地图的网络模型构造如下:

1. 将网格中的每个非障碍点分成(x,y)两个点(x,y,0)和(x,y,1),其中源点s = v(1, 1, 0),汇点t = v(maxx, maxy, 1)。

2. 在以上顶点中添加以下三种类型的边e1,e2,e3,相应地容量和费用分别记为c1、c2、c3以及w1、w2、w3:

u e1 = v(x, y, 0) -> v(x, y, 1),c1 = maxint,w1 = 0。
u e2 = v(x, y, 0) -> v(x, y, 1),c2 = 1,w2 = -1(这里要求(x, y)必须是矿石)
u e3 = v(x, y, 1) -> v(x'', y'', 0),c3 = maxint,w3 = 0.

其中x''=x+1 y''=y 或x''=x y''=y+1,1 <= x'' <= maxx,1 <= y'' <= maxy,且(x'' y'')非障碍。

从以上模型中可以看出,在构造的过程中,将地图上的一个点"拆"成了网络的两个节点。添加e1型边使得每个点可以被多次访问,而添加e2型边使得某点上的矿石对于这个网络,从s到t的一条路径可以看作是一辆探测车的行动路线。路径费用就是探测车搜集到的矿石的数目。对于网络g求流量为numberofvehicles的固定最小费用流,可以得到问题的解。

[模型二]

事实上,如果我们只考虑这numberofvehicles辆车中每辆车分别依次装上哪些矿石。则每辆车经过的矿石就是一条流,因此我们以网格中的矿石为网络的顶点建立以下的网络流模型。

1. 将网格中的每个起点(网格左上角)能到达,且能从它能到达终点(右下角)的矿石 (x,y)点分成左点(x,y,0)和右点(x,y,1)两个点,并添加源点s和汇点t。
2. 在以上顶点中添加以下五种类型的边e1,e2,e3,相应地容量和费用分别记为c1、c2、c3以及w1、w2、w3:

u e1 = v(x, y, 0) -> v(x, y, 1),c1 = 1,w1 = -1。
u e2 = v(x, y, 1) -> v(x'', y'', 0),c2 = 1,w2 = 0(矿石点(x, y)可到达矿石点(x'',y''))。
u e3 = s -> v(x, y, 0),c3 = 1,w3 = 0。
u e4 = v(x, y, 1)->t,c4 = 1,w4 = 0。
u e5=s->t,c5=maxint,w5=0。

由于每个石块被折成两个点,且容量为1,就保证了每个石块只被取走一次,同时取走一块石块就得到-1的费用。因此对以上模型,我们求流量为numberofvehicles的最小费用流,就可得到解。

[两种模型的比较]

1.模型一以网格为顶点,模型二以矿石为顶点,因此在顶点个数上模型二明显优于模型一,对于一些矿石比较稀疏,而网格又比较大的数据,模型二的效率要比模型一来得高。且只要矿石的个数不超过一定数目,模型二可以处理p,q很大的数据,而模型一却不行。

2.模型一中边的数目最多为3*p*q,而模型二中边的数目最坏情况下大约为p*q*(p+1)*(q+1)/4-p*q。因此在这个问题中,若对于一些矿石比较密集且网格又比较大的数据,模型二的边数将大大超过模型一,从而使得时间效率大大低于模型一。

下面是网格中都是矿石的情况比较(piii700/128m ,bp7.0保护模式):
numberofvehicles=10 模型一 模型二

通过以上数据,可知对于p,q不超过60的情况,模型一都能在10秒内出解。而模型二则对于p、q=30的最坏情况下速度就很慢了,且p、q超过30后就出现内存溢出情况,而无法解决。

因此,对于本题,以上两种模型各有利弊,我们可根据测试数据中矿石稀疏程度来决定建立什么样的模型。若矿石比较稀疏,则可以考虑用建立如模型二的网络模型;若矿石比较密集则建立模型一所示网络模型。然后,再应用求最小费用最大流算法求解。对于p,q>60,且矿石比较多情况下,两种模型的网络流算法都无法求解。在实际的应用中问题经常都只要求近似解,此时还可用综合一些其它算法来求解。

四、结束语

综上所述,网络流算法中模型的优化是网络流算法提高效率的根本。我们要根据实际问题,从减少顶点及边的角度综合考虑如何对模型进行优化,选择适当的模型,以提高算法的效率。对于有些题目,解题的各种模型各有优劣时,还可通过程序自动分析测试数据,以决定何种情况下采用何种模型,充分发挥各种模型的优点,以达到优化程序效率的目的。

‘贰’ c/c++ 最大流算法ford-fulkerson

你的问题是用C/C++写最大流算法ford-fulkerson算法。顶点就是节点。

void maximum_flow(int n, int s, int t, int *capacity, int *flow)

可以参考:算法模板-最大流(Ford-fulkerson算法)

‘叁’ 网络流之最大流,您只需判断这个代码是属于哪一种最大流算法即可。

Edmonds - Karp 算法
最简单的增广路类算法,每次用一个 BFS 寻找最短增广路
while(1) 里前半部分的 for 循环就是 BFS 部分,队列 que[] 辅助进行 BFS,找到的增广路存在 pre[i] 中
if(!pre[sink])判断是否存在可到达汇点的增广路,不存在就跳出循环
后半部分 for 循环对找到的路径进行增广操作。

时间复杂度 O(VE^2),行数虽少,但效率不是很高的算法

最后说一句,这代码风格太差了 = =,只考虑代码长度完全不顾可读性

参考资料是自己的 blog 呵呵

‘肆’ 网络流的最大流算法

1、augment path,直译为“增广路径”,其思想大致如下:
原有网络为G,设有一辅助图G',其定义为V(G') = V(G),E(G')初始值(也就是容量)与E(G)相同。每次操作时从Source点搜索出一条到Sink点的路径,然后将该路径上所有的容量减去该路径上容量的最小值,然后对路径上每一条边<u,v>添加或扩大反方向的容量,大小就是刚才减去的容量。一直到没有路为止。此时辅助图上的正向流就是最大流。
我们很容易觉得这个算法会陷入死循环,但事实上不是这样的。我们只需要注意到每次网络中由Source到Sink的流都增加了,若容量都是整数,则这个算法必然会结束。
寻找通路的时候可以用DFS,BFS最短路等算法。就这两者来说,BFS要比DFS快得多,但是编码量也会相应上一个数量级。
增广路方法可以解决最大流问题,然而它有一个不可避免的缺陷,就是在极端情况下每次只能将流扩大1(假设容量、流为整数),这样会造成性能上的很大问题,解决这个问题有一个复杂得多的算法,就是预推进算法。
2、push label,直译为“预推进”算法。
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),还有一个叫Highest Label Preflow Push的算法复杂度据说是O(V^2*E^0.5)。我研究了一下HLPP,感觉它和Relabel-to-Front本质上没有区别,因为Relabel-to-Front每次前移的都是高度最高的顶点,所以也相当于每次选择最高的标号进行更新。还有一个感觉也会很好实现的算法是使用队列维护溢出顶点,每次对pop出来的顶点discharge,出现了新的溢出顶点时入队。
Push-Relabel类的算法有一个名为gap heuristic的优化,就是当存在一个整数0<k<V,没有任何顶点满足h[v]=k时,对所有h[v]>k的顶点v做更新,若它小于V+1就置为V+1。
cpp程序: #include<cstdio>#include<cstring>#include<algorithm>#include<queue>#;inttt,kase;intnn,m;intH[45],X[1004],P[1004],flow[1004],tot,cap[1005];intd[45];intS,T;voidadd(intx,inty,intz){P[++tot]=y;X[tot]=H[x];H[x]=tot;flow[tot]=z;cap[tot]=flow[tot];}queue<int>q;boolbfs(){memset(d,0,sizeof(d));d[S]=1;intx;q.push(S);while(!q.empty()){x=q.front();q.pop();for(inti=H[x];i;i=X[i]){if(flow[i]>0&&!d[P[i]]){d[P[i]]=d[x]+1;q.push(P[i]);}}}returnd[T];}intdfs(intx,inta){if(x==T||a==0)returna;intf=a,tmp;for(inti=H[x];i;i=X[i]){if(flow[i]>0&&d[P[i]]==d[x]+1){tmp=dfs(P[i],min(flow[i],a));flow[i]-=tmp;a-=tmp;flow[i^1]+=tmp;if(!a)break;}}if(f==a)d[x]=-1;returnf-a;}intDinic(){intf=0;while(bfs())f+=dfs(S,inf);returnf;}intmain(){/**输入过程省略**/intmaxflow=Dinic();printf(%d ,maxflow);return0;}

阅读全文

与最大流算法代码相关的资料

热点内容
神奇高量战法副图源码 浏览:828
汇编语言设计凯撒密码加密器 浏览:388
主次梁加密是加在哪里 浏览:662
模板匹配算法matlab 浏览:823
外地程序员去北京 浏览:22
安卓机换苹果12如何转移数据 浏览:418
互联网ntp服务器地址及端口 浏览:613
pdf到word转换器 浏览:267
飞行解压素材 浏览:498
51单片机指令用背吗 浏览:936
unityai算法 浏览:834
我的世界ice服务器如何打开pvp 浏览:975
c语言编程如何做标记 浏览:884
python数据分析实战pdf 浏览:985
u盘插入文件夹 浏览:918
华为amd云服务器 浏览:497
汉化编程卡是什么意思 浏览:128
python学习pdf 浏览:316
祝绪丹程序员那么可爱拍吻戏 浏览:200
asp源码会员消费系统 浏览:115