Edmonds-karp算法
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
1. 最大流最小割定理介绍:
把一个流网络的顶点集划分成两个集合S和T,使得源点s ∈S且汇点t ∈T,割(S,T)的容量C(S,T) =∑Cuv, 其中u∈S且v∈T。
从直观上看,截集(S,T)是从源点s到汇点t的必经之路,如果该路堵塞则流从s无法到达t。
于是我们可以得到下面的定理:
最大流最小割定理:
任意一个流网络的最大流量等于该网络的最小的割的容量。
这个定理的证明这里就不给出了,可以参考图论方面的资料。
2. 求最大流的Edmonds-Karp算法简介:
若给定一个可行流F=(Fij),我们把网络中使Fij=Cij的弧称为饱和弧,Fij<Cij的弧称为未饱和弧。
如果流网络中从i到j没有弧,我们添加一条从i到j且容量Cij=0的弧,这样整个流网络变成一个完全图。
如果从i到j有流量Fij,则从j到i的流量定义为Fji = -Fij 。
考虑一条从源点s出发到汇点t的路径p,如果对于每一段弧(i,j)属于p都有Fij < Cij,即每一条属于p的弧都是未饱和弧,则我们可以向这条路径上压入更多的流,使得其中的一条弧达到饱和。
这样的路径p叫做可改进路,可压入的流量叫做该可改进路的可改进流量。
重复这个过程,直到整个网络找不到一条可改进路,显然这时候网络的流量达到最大。
Edmonds-Karp算法就是利用宽度优先不断地找一条从s到t的可改进路,然后改进流量,一直到找不到可改进路为止。
由于用宽度优先,每次找到的可改进路是最短的可改进路,通过分析可以知道其复杂度为O(VE2)。
Edmonds-Karp算法的伪代码如下:
设队列Q--存储当前未检查的标号点,队首节点出队后,成为已检查的标点;
path -- 存储当前已标号可改进路经;
repeat
path置空;
源点s标号并进入path和Q;
while Q非空and 汇点t未标号do
begin
移出Q的队首顶点u;
for 每一条从u出发的弧(u,v) do
if v未标号and 弧(u,v)的流量可改进
then v进入队列Q和path;
end while
if 汇点已标号
then 从汇点出发沿着path修正可改进路的流量;
until 汇点未标号;
Edmonds-Karp算法有一个很重要的性质:当汇点未标号而导致算法结束的时候,那些已经标号的节点构成集合S,未标号的节点构成集合T,割(S,T)恰好是该流网络的最小割;且这样求出的最小割(S,T)中集合S的元素数目一定是最少的。
寻找最大流的基本方法是Ford-Fulkerson方法,该方法有多种实现,其基本思想是从某个可行流F出发,找到关于这个流的一个可改进路经P,然后沿着P调整F,对新的可行流试图寻找关于他的可改进路经,如此反复直至求得最大流。
现在要找最小费用的最大流,可以证明,若F 是流量为V(F)的流中费用最小者,而P是关于F的所有可改进路中费用最小的可改进路,则沿着P去调整F,得到的可行流F'一定是流量为V(F')的所有可行流中的最小费用流。
这样,当F 是最大流时候,他就是所要求的最小费用最大流。
注意到每条边的单位流量费用B(i,j)≥0,所以F=0必是流量为0的最小费用流,这样总可以从F=0出发求出最小费用最大流。
一般的,设已知F是流量V(F)的最小费用流,余下的问题就是如何去寻找关于F的最小费用可改进路。
为此我们将原网络中的每条弧<u,v>变成两条方向相反的弧:
1。
前向弧<u,v>,容量C和费用B不变,流量F为0;
2。
后向弧<v,u>,容量C为0,费用为-B,流量F为0;
每一个顶点上设置一个参数CT,表示源点至该顶点的通路上的费用和。
如果我们得出一条关于F的最小费用可改进路时,则该路上的每一个顶点的CT值相对于其它可改进路来说是最小的。
每一次寻找最小费用可改进路时前,源点的CT为0,其它顶点的CT为+∞。
设cost为流的运输费用,初始时由于F=0,则cost=0,我们每求出一条关于F的最小费用可改进路,则通过cost ←cost + ∑B(e)*d, (其中e∈P,d为P的可改进量)来累积流的运输费用的增加量。
显然,当求出最小费用最大流时,cost便成为最大流的运输费用了。
另外设置布尔变量break为最小费用可改进路的延伸标志,在搜索了网络中的每一个顶点后,若break=true表示可改进路还可以延伸,还需要重新搜索网络中的顶点;否则说明最小费用的可改进路已经找到或者最大流已经求出。
本人说明:
这个模版的代码完全按照BFS从源点逐个遍历增广路径,得到最大增广容量,通过不断调整,最后求得最大流量,值得注意的是,最后一次BFS后所标的路线即为最小截集,即所谓的瓶颈,据此很容易求出最小截集的容量。
const limit=1000;
var ca:array[1..limit,1..limit]of longint;
n,m,total:longint;
function min(a,b:longint):longint;
begin
if a<b then min:=a else min:=b;
end;
procedure init;
var i,j,x,y:longint;
begin
readln(n,m);
fillchar(ca,sizeof(ca),0);
for i:=1 to n do
begin
readln(x,y,j);
inc(ca[x,y],j);
end;
end;
procedure work;
var pre,flow:array[1..limit]of longint;
visited:array[1..limit]of boolean;
max,maxl,i,j,path:longint;
begin
total:=0;
repeat
fillchar(pre,sizeof(pre),0);
fillchar(flow,sizeof(flow),0);
fillchar(visited,sizeof(visited),0);
flow[1]:=maxlongint;
while true do
begin
max:=0;
maxl:=0;
for i:=1 to m do
if (not visited[i])and(flow[i]>max) then begin
max:=flow[i];
maxl:=i;
end;
if (maxl=0)or(maxl=m) then break;
visited[maxl]:=true;
for i:=1 to m do
if flow[i]<min(max,ca[maxl,i]) then
begin
pre[i]:=maxl;
flow[i]:=min(max,ca[maxl,i]);
end;
end;
if maxl=0 then break;
path:=flow[m];
inc(total,path);
i:=m;
while i<>1 do
begin
j:=pre[i];
dec(ca[j,i],path);
inc(ca[i,j],path);
i:=j;
end;
until false; end;
procedure print; begin
writeln(total); end;
begin
init;
work;
print;
end.。