算法学习图论之网络流问题算法小结Word格式.docx
- 文档编号:19472563
- 上传时间:2023-01-06
- 格式:DOCX
- 页数:12
- 大小:69.06KB
算法学习图论之网络流问题算法小结Word格式.docx
《算法学习图论之网络流问题算法小结Word格式.docx》由会员分享,可在线阅读,更多相关《算法学习图论之网络流问题算法小结Word格式.docx(12页珍藏版)》请在冰豆网上搜索。
E-K算法的时间复杂度为O(VE2),由于BFS要搜索全部小于最短距离的分支路径之后才能找到终点,因此可以想象频繁的BFS效率是比较低的。
实践中此算法使用的机会较少。
3.Dinic算法
BFS寻找终点太慢,而DFS又不能保证找到最短路径。
1970年Dinic提出一种思想,结合了BFS与DFS的优势,采用构造分层网络的方法可以较快找到最短增广路,此算法又称为阻塞流算法(BlockingFlowAlgorithm)。
首先定义分层网络AN(f)。
在残量网络中从源点s起始进行BFS,这样每个顶点在BFS树中会得到一个距源点s的距离d,如d(s)=0,直接从s出发可到达的点距离为1,下一层距离为2...。
称所有具有相同距离的顶点位于同一层,在分层网络中,只保留满足条件d(i)+1=d(j)的边,这样在分层网络中的任意路径就成为到达此顶点的最短路径。
Dinic算法每次用一遍BFS构建分层网络AN(f),然后在AN(f)中一遍DFS找到所有到终点t的路径增广;
之后重新构造AN(f),若终点t不在AN(f)中则算法结束。
DFS部分算法可描述如下:
1p<
--s
2whiles的出度>
0do
u<
--p.top
ifu!
=tthen
ifu的出度>
0then
设(u,v)为AN(f)中一条边
7
p<
--p,v
8
else
9
从p和AN(f)中删除点u以及和u连接的所有边
10
11
沿p增广
12
令p.top为从s沿p可到达的最后顶点
13endwhile
实际代码中不必真的用一个图来存储分层网络,只需保存每个顶点的距离标号并在DFS时判断dist[i]+1=dist[j]即可。
Dinic的时间复杂度为O(V2E)。
由于较少的代码量和不错的运行效率,Dinic在实践中比较常用。
具体代码可参考DD_engi网络流算法评测包中的标程,这几天dinic算法的实现一共看过和比较过将近10个版本了,DD写的那个在效率上是数一数二的,逻辑上也比较清晰。
4.ImprovedSAP算法
本次介绍的重头戏。
通常的SAP类算法在寻找增广路时总要先进行BFS,BFS的最坏情况下复杂度为O(E),这样使得普通SAP类算法最坏情况下时间复杂度达到了O(VE2)。
为了避免这种情况,Ahuja和Orlin在1987年提出了ImprovedSAP算法,它充分利用了距离标号的作用,每次发现顶点无出弧时不是像Dinic算法那样到最后进行BFS,而是就地对顶点距离重标号,这样相当于在遍历的同时顺便构建了新的分层网络,每轮寻找之间不必再插入全图的BFS操作,极大提高了运行效率。
国内一般把这个算法称为SAP...显然这是不准确的,毕竟从字面意思上来看E-K和Dinic都属于SAP,我还是习惯称为ISAP或改进的SAP算法。
与Dinic算法不同,ISAP中的距离标号是每个顶点到达终点t的距离。
同样也不需显式构造分层网络,只要保存每个顶点的距离标号即可。
程序开始时用一个反向BFS初始化所有顶点的距离标号,之后从源点开始,进行如下三种操作:
(1)当前顶点i为终点时增广
(2)当前顶点有满足dist[i]=dist[j]+1的出弧时前进(3)当前顶点无满足条件的出弧时重标号并回退一步。
整个循环当源点s的距离标号dist[s]>
=n时结束。
对i点的重标号操作可概括为dist[i]=1+min{dist[j]:
(i,j)属于残量网络Gf}。
具体算法描述如下:
algorithmImproved-Shortest-Augmenting-Path
1f<
2从终点t开始进行一遍反向BFS求得所有顶点的起始距离标号d(i)
3i<
4whiled(s)<
n
do
ifi=tthen
//找到增广路
Augment
i<
--s
//从源点s开始下次寻找
ifGf包含从i出发的一条允许弧(i,j)
thenAdvance(i)
elseRetreat(i)
//没有从i出发的允许弧则回退
11returnf
procedureAdvance(i)
1设(i,j)为从i出发的一条允许弧
2pi(j)<
--i
//保存一条反向路径,为回退时准备
--j
//前进一步,使j成为当前结点
procedureRetreat(i)
1d(i)<
--1+min{d(j):
(i,j)属于残量网络Gf}
//称为重标号的操作
2ifi!
=s
theni<
--pi(i)
//回退一步
procedureAugment
1pi中记录为当前找到的增广路P
2delta<
(i,j)属于P}
3沿路径P增广delta的流量
4更新残量网络Gf
算法中的允许弧是指在残量网络中满足dist[i]=dist[j]+1的弧。
Retreat过程中若从i出发没有弧属于残量网络Gf则把顶点距离重标号为n。
虽然ISAP算法时间复杂度与Dinic相同都是O(V2E),但在实际表现中要好得多。
要提的一点是关于ISAP的一个所谓GAP优化。
由于从s到t的一条最短路径的顶点距离标号单调递减,且相邻顶点标号差严格等于1,因此可以预见如果在当前网络中距离标号为k(0<
=k<
n)的顶点数为0,那么可以知道一定不存在一条从s到t的增广路径,此时可直接跳出主循环。
在我的实测中,这个优化是绝对不能少的,一方面可以提高速度,另外可增强ISAP算法时间上的稳定性,不然某些情况下ISAP会出奇的费时,而且大大慢于Dinic算法。
相对的,初始的一遍BFS却是可有可无,因为ISAP可在循环中自动建立起分层网络。
实测加不加BFS运行时间差只有5%左右,代码量可节省15~20行。
5.最大容量路径算法(MaximumCapacityPathAlgorithm)
1972年还是那个E-K组合提出的另一种最大流算法。
每次寻找增广路径时不找最短路径,而找容量最大的。
可以预见,此方法与SAP类算法相比可更快逼近最大流,从而降低增广操作的次数。
实际算法也很简单,只用把前面E-K算法的BFS部分替换为一个类Dijkstra算法即可。
USACO4.2节的说明详细介绍了此算法,这里就不详述了。
时间复杂度方面。
BFS是O(E),简单Dijkstra是O(V2),因此效果可想而知。
但提到Dijkstra就不能不提那个Heap优化,虽然USACO的算法例子中没有用Heap,我自己还是实现了一个加Heap的版本,毕竟STL的优先队列太好用了不加白不加啊。
效果也是非常明显的,但比起Dinic或ISAP仍然存在海量差距,这里就不再详细介绍了。
6.CapacityScalingAlgorithm
不知道怎么翻比较好,索性就这么放着吧。
叫什么的都有,容量缩放算法、容量变尺度算法等,反正就那个意思。
类似于二分查找的思想,寻找增广路时不必非要局限于寻找最大容量,而是找到一个可接受的较大值即可,一方面有效降低寻找增广路时的复杂度,另一方面增广操作次数也不会增加太多。
时间复杂度O(E2logU)实际效率嘛大约稍好于最前面BFS的E-K算法,稀疏图时表现较优,但仍然不敌Dinic与ISAP。
7.算法效率实测!
重头戏之二,虽然引用比较多,哎~
首先引用此篇强文《MaximumFlow:
AugmentingPathAlgorithmsComparison》
对以上算法在稀疏图、中等稠密图及稠密图上分别进行了对比测试。
直接看结果吧:
稀疏图:
ISAP轻松拿下第一的位置,图中最左边的SAP应该指的是E-K算法,这里没有比较Dinic算法是个小遗憾吧,他把Dinic与SAP归为一类了。
最大流量路径的简单Dijkstra实现实在是太失败了--,好在Heap优化后还比较能接受……可以看到Scaling算法也有不错的表现。
中等稠密图:
ISAP依然领先。
最大流量算法依然不太好过……几个Scaling类算法仍然可接受。
稠密图:
ISAP……你无敌了!
这次可以看出BFS的Scaling比DFS实现好得多,而且几乎与ImprovedScaling不相上下,代码量却差不少。
看来除ISAP外BFSScaling也是个不错的选择。
最后补个我自己实测的图,比较算法有很多是DD网络流算法评测包里的标程,评测系统用的Cena,评测数据为DDditch数据生成程序生成的加强版数据:
我一共写了7个版本的ISAP,Dinic算法也写了几个递归版的但效率都不高,只放上来一个算了。
从上图来看似乎ISAP全面超越了号称最大流最快速算法的HLPP,但在另外一台机器上测试结果与此却不大相同,有时ISAP与HLPP基本持平,有时又稍稍慢一些。
在这种差距非常小的情况下似乎编译器的效果也比较明显。
那个HLPP是用PASCAL写的,我不知在Win32平台下编译代码效率如何,至少我的几个ISAP用VC2008+SP1编译比用g++要强不少,也可能是参数设置的问题。
不过这些都是小事,关键是见证了ISAP的实际效率。
从上面可以看出不加GAP优化的ISAP有几个测试点干脆无法通过,而不加BFS却无甚大影响,递归与非递归相差在7%左右的样子。
综合以上表现,推荐采用ISAP不加BFS,非递归+GAP优化的写法,Ditch这道题一共也才80行左右代码。
要提的一点是GAP优化用递归来表现的话不如while循环来得直接。
另外,看起来HLPP表现确实很优秀,有机会也好好研究一下吧,预流推进重标号算法也是一大类呢……
最后附上一个ISAP+GAP+BFS的非递归版本代码,网络采用邻接表+反向弧指针:
1.#include<
cstdio>
2.#include<
memory>
3.usingnamespacestd;
4.
5.constintmaxnode=1024;
6.constintinfinity=2100000000;
7.
8.structedge{
9.
intver;
//vertex
10.
intcap;
//capacity
11.
intflow;
//currentflowinthisarc
12.
edge*next;
//nextarc
13.
edge*rev;
//reversearc
14.
edge(){}
15.
edge(intVertex,intCapacity,edge*Next)
16.
:
ver(Vertex),cap(Capacity),flow(0),next(Next),rev((edge*)NULL){}
17.
void*operatornew(size_t,void*p){
18.
returnp;
19.
}
20.}*Net[maxnode];
21.intdist[maxnode]={0},numbs[maxnode]={0},src,des,n;
22.//dist:
层次标号numbs:
23.
24.voidrev_BFS(){
25.
intQ[maxnode],head=0,tail=0;
26.
for(inti=1;
i<
=n;
++i){
27.
dist[i]=maxnode;
28.
numbs[i]=0;
29.
30.
31.
Q[tail++]=des;
32.
dist[des]=0;
//终点的标号清零
33.
numbs[0]=1;
34.
while(head!
=tail){
35.
intv=Q[head++];
//enqueue
36.
for(edge*e=Net[v];
e;
e=e->
next){
37.
if(e->
rev->
cap==0||dist[e->
ver]<
maxnode)continue;
38.//由终点方向指向开始点方向的边||访问过的点continue
39.
dist[e->
ver]=dist[v]+1;
40.
++numbs[dist[e->
ver]];
//每个层次的点数计数,GAP优化的一部分,当为0时退出
41.
Q[tail++]=e->
ver;
//inqueue
42.
43.
44.}
45.
46.intmaxflow(){
47.
intu,totalflow=0;
48.
edge*CurEdge[maxnode],*revpath[maxnode];
49.
++i)CurEdge[i]=Net[i];
50.
u=src;
51.
while(dist[src]<
n){
52.
if(u==des){
//findanaugmentingpath
53.
intaugflow=infinity;
54.
for(inti=src;
i!
=des;
i=CurEdge[i]->
ver)
55.
augflow=min(augflow,CurEdge[i]->
cap);
56.
ver){
57.
CurEdge[i]->
cap-=augflow;
58.
cap+=augflow;
59.
flow+=augflow;
60.
flow-=augflow;
61.
}//对增广路上的正向弧与反向弧进行操作
62.
totalflow+=augflow;
63.
64.
65.
66.
edge*e;
67.
for(e=CurEdge[u];
next)
68.
cap>
0&
&
dist[u]==dist[e->
ver]+1)break;
69.
if(e){
//findanadmissiblearc,thenAdvance
70.
CurEdge[u]=e;
71.
revpath[e->
ver]=e->
rev;
72.
u=e->
73.
}else{
//noadmissiblearc,thenrelabelthisvertex
74.
if(0==(--numbs[dist[u]]))break;
//GAPcut,Important!
75.
CurEdge[u]=Net[u];
76.
intmindist=n;
77.
for(edge*te=Net[u];
te;
te=te->
78.
if(te->
0)mindist=min(mindist,dist[te->
ver]);
79.
dist[u]=mindist+1;
80.
++numbs[dist[u]];
81.
if(u!
=src)
82.
u=revpath[u]->
//Backtrack
83.
84.
85.
returntotalflow;
86.}
87.
88.intmain(){
89.
intm,u,v,w;
90.
freopen("
ditch.in"
"
r"
stdin);
91.
ditch.out"
w"
stdout);
92.
while(scanf("
%d%d"
&
m,&
n)!
=EOF){
//POJ1273needthiswhileloop
93.
edge*buffer=newedge[2*m];
94.
edge*data=buffer;
95.
src=1;
des=n;
96.
while(m--){
97.
scanf("
%d%d%d"
u,&
v,&
w);
98.
Net[u]=new((void*)data++)edge(v,w,Net[u]);
99.//相当于ArcNode*p=newArcNode(v2,weight,V[v1].firstarc);
100.//V[v1].firstarc=p;
两句代码
101.
Net[v]=new((void*)data++)edge(u,0,Net[v]);
102.
Net[u]->
rev=Net[v];
103.
Net[v]->
rev=Net[u];
104.
}
105.
rev_BFS();
106.
printf("
%d\n"
maxflow());
107.
delete[]buffer;
108.
109.
return0;
110.}
SAP几种优化
概述:
最短增广路算法(ShortestAugmentingPathAlgorithm),即每次
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 算法 学习 网络 问题 小结
