最小费用最大流 zkw算法

最小费用最大流的算法有很多种,经典的就是EK+SPFA。但是时间效率确实令人不敢恭维~

最近小小的学习了一下传说中的费用流的神算法——zkw算法。(P.s. 以其发明者zkw神牛的名字命名)

 

该算法可以有效地通过修改距离标号的方法避免反复进行最短路运算的部分省去,基本算法类似KM算法的顶标修改。基本思想就是:采用距离标号法,若能增广则无限增广,反之更新距离标号,直到无法更新为止。

本人的嘴比较笨,这里直接引用zkw神牛的原文。

 

【以下为zkw原文:】

最小费用流在 OI 竞赛中应当算是比较偏门的内容,但是 NOI2008 中 employee 的突然出现确实让许多人包括 zkw 自己措手不及。可怜的 zkw 当时想出了最小费用流模型,可是他从来没有实现过,所以不敢写,此题 0 分。zkw 现在对费用流的心得是:虽然理论上难,但是写一个能 AC 题的费用流还算简单。先贴一个我写的 employee 程序:只有不到 70 行,费用流比最大流还好写~

 

 这里使用的是连续最短路算法。最短路算法?为什么程序里没有 SPFA?Dijkstra?且慢,先让我们回顾一下图论中最短路算法中的距离标号。定义  为点  的距离标号,任何一个最短路算法保证,算法结束时对任意指向顶点  、从顶点  出发的边满足  (条件1),且对于每个  存在一个  使得等号成立(条件2)。换句话说,任何一个满足以上两个条件的算法都可以叫做最短路,而不仅仅是 SPFA、Dijkstra,算法结束后,恰在最短路上的边满足  

  在最小费用流的计算中,我们每次沿  的路径增广后都不会破坏条件 1,但是可能破坏了条件 2。不满足条件 2 的后果是什么呢?使我们找不到每条边都满足  新的增广路。只好每次增广后使用 Dijkstra,SPFA 等等算法重新计算新的满足条件 2 的距离标号。这无疑是一种浪费。KM 算法中我们可以修改不断修改可行顶标,不断扩大可行子图,这里也同样,我们可以在始终满足条件 1 的距离标号上不断修改,直到可以继续增广(满足条件 2)。 

  回顾一下 KM 算法修改顶标的方法。根据最后一次寻找交错路不成功的 DFS,找到  ,左边的点增加  ,右边的点减少  。这里也一样,根据最后一次寻找增广路不成功的 DFS,找到  ,所有访问过的点距离标号增加 。可以证明,这样不会破坏性质 1,而且至少有一条新的边进入了  的子图。 

  算法的步骤就是初始标号设为  ,不断增广,如果不能增广,修改标号继续增广,直到彻底不能增广:源点的标号已经被加到了   

  这样我们得到了一个简单的算法,只需要增广,改标号,各自只有 7 行,不需要 BFS,队列,SPFA,编程复杂度很低。由于实际的增广都是沿最短路进行的,所以理论时间复杂度与使用 SPFA 等等方法的连续最短路算法一致,但节省了 SPFA 或者 Dijkstra 的运算时间。实测发现这种算法常数很小,速度较快,employee 这道题所有数据加在一起耗时都在 2s 之内。由于没有找到参考程序,与 SPFA 实现的速度比较大家可以自己评测。 

 

原文地址:http://www.mathlinks.ro/weblog_entry.php?t=256087 

 

下面贴一下本人奇丑无比的代码:

主程序部分:

repeat now:=g; while aug(s,inf)>0 do fillchar(v,sizeof(v),0); until modlabel;

增广路过程:

function aug(x,flow:longint):longint; var p,tmp:longint; begin if x=t then begin inc(ans,flow*dis[s]); exit(flow); end; v[x]:=true; p:=now[x]; while p<>0 do begin if (not v[node[p].data]) and (node[p].c>0) and (dis[node[p].data]+node[p].cost=dis[x]) then begin tmp:=aug(node[p].data,min(node[p].c,flow)); if tmp>0 then begin now[x]:=p; dec(node[p].c,tmp); inc(node[node[p].op].c,tmp); exit(tmp); end; end; p:=node[p].next; end; now[x]:=0; exit(0); end;

顶标调整过程:

function modlabel:boolean; var i,p:longint; delta:longint; begin delta:=inf; for i:=s to t do if v[i] then begin p:=g[i]; while p<>0 do begin if not v[node[p].data] and (node[p].c>0) then delta:=min(delta,dis[node[p].data]+node[p].cost-dis[i]); p:=node[p].next; end; end; if delta=inf then exit(true); for i:=s to t do if v[i] then inc(dis[i],delta); fillchar(v,sizeof(v),0); exit(false); end;

你可能感兴趣的:(学习笔记)