网络流算法

hy_bug 2001-08-26 06:04:06
加精
谁贴一份上来?
...全文
330 9 打赏 收藏 转发到动态 举报
写回复
用AI写文章
9 条回复
切换为时间正序
请发表友善的回复…
发表回复
jhhxxdj 2002-01-19
  • 打赏
  • 举报
回复
可惜
hy_bug 2001-08-27
  • 打赏
  • 举报
回复
谢了
starfish 2001-08-26
  • 打赏
  • 举报
回复
看看图论的书,都差不多,没有特别好的,多看几本吧
newtoon2002 2001-08-26
  • 打赏
  • 举报
回复
to starfish(海星) :
厉害!网络流看那本书比较好?
starfish 2001-08-26
  • 打赏
  • 举报
回复
/********************************************** *
* 图论算法 *
* *
* copyright starfish *
* 2000/10/24 *
* *
\*********************************************/

#define infinity 1000000 // a big int
#define max_vertexes 50 // the max count of vertexes

typedef int Graph[max_vertexes][max_vertexes]; // use adjacent matrix to represent graph

/*********************************************
求最小生成树的Prim算法
用邻接矩阵表示图,复杂度为O(n^2)
参数G表示图的邻接矩阵,
vcount表示图的顶点个数,
father用来记录每个节点的父节点
**********************************************/
void prim(Graph G,int vcount,int father[])
{
int i,j,k;
int lowcost[max_vertexes], closeset[max_vertexes], used[max_vertexes];
for( i = 0; i < vcount; i++ )
{
lowcost[i] = G[0][i];
closeset[i] = 0; // notice: here vertex 1 is G[0]
used[i] = 0; // mark all vertexes have not been used
father[i] = -1; // that means no father
}
used[0] = 1; // mark vertex 1 has been used
for( i=1; i< vcount; i++)
{
j=0;
while( used[j] ) j++;
for(k=0; k<vcount; k++)
if ( ( !used[k] ) && ( lowcost[k] < lowcost[j] ) )
{
j=k; // find the next tree edge
}
father[j]=closeset[j]; // record the tree edge using father array
used[j]=1; // mark vertex j+1 has been used
for (k=0;k<vcount;k++)
if (!used[k]&&(G[j][k]<lowcost[k])) // modify the lowcost
{
lowcost[k]=G[j][k];
closeset[k]=j;
}
}
}

/*===============================================

单源最短路径

Dijkstra 算法

适用条件:所有边的权非负

!!注意:
1.输入的图的权必须非负
2.顶点标号从0开始
3.当i,j不相邻时G[i,j]=infinity

================================================*/
int Dijkstra(Graph G,int n,int s,int t, int path[])
{
int i,j,w,minc, d[max_vertexes], mark[max_vertexes];
for (i=0; i<n; i++) mark[i]=0;

for (i=0; i<n; i++)
{
d[i]=G[s][i];
path[i]=s;
}

mark[s]=1; path[s]=0; d[s]=0;

for(i=1; i<n; i++)
{
minc = infinity;
w = 0;
for( j = 0; j < n; j++ )
if( ( mark[j]==0 ) && ( minc >= d[j] ) ) {
minc=d[j];w=j;
}
mark[w]=1;
for(j=0; j<n; j++)
if( (mark[j]==0) && ( G[w][j] != infinity ) && ( d[j] > d[w]+G[w][j] ) )
{
d[j]=d[w]+G[w][j];
path[j]=w;
}
}
return d[t];
}

/*======================================================

单源最短路径

Bellman-Ford 算法

适用条件:所有情况,边权可以为负
G为图,n为图的节点数目,s,t分别为源点和终点,
success用来标示该函数是否成功,
如果存在一个从源点可达的权为负的回路则success=false;
返回值为s,t之间的最短路径长度;


!!注意:
1.顶点标号从0开始
2.当i,j不相邻时G[i,j]=infinity

=============================================================*/

int Bellman_Ford(Graph G,int n,int s,int t,int path[],int success)
{
int i,j,k,d[max_vertexes];
for (i=0;i<n;i++) {
d[i]=infinity;
path[i]=0;
}
d[s]=0;
for (k=1;k<n;k++)
for (i=0;i<n;i++)
for (j=0;j<n;j++)
if( d[j] > d[i]+G[i][j] ) {
d[j] = d[i]+G[i][j];
path[j] = i;
}
success=0;

for (i=0;i<n;i++)
for (j=0;j<n;j++)
if( d[j] > d[i]+G[i][j] ) return 0;

success=1;
return d[t];
}

/*==========================================

每对节点间最短路径
Floyd-Warshall 算法

D[i,j]表示从i到j的最短距离;
P[i,j]表示从i到j的最短路径上j 的父节点

===========================================*/

void Floyd_Washall(Graph G,int n,Graph D,Graph P)
{
int i,j,k;
for (i=0;i<n;i++)
for (j=0;j<n;j++) {
D[i][j] = G[i][j];
P[i][j] = i;
}
for (i=0;i<n;i++) {
D[i][i] = 0;
P[i][i] = 0;
}

for (k=0;k<n;k++)
for (i=0;i<n;i++)
for (j=0;j<n;j++)
if ( D[i][j] > D[i][k] + D[k][j] ) {
D[i][j] = D[i][k] + D[k][j];
P[i][j] = P[k][j];
}
}





////////////////////////////////////
//
// 图论算法
//
// copyright starfish
//
////////////////////////////////////



{===============================================

单源最短路径

Dijkstra 算法

适用条件:所有边的权非负
其中path数组的下标可以从1开始算起,
因为path数组的长度为size+1,path[0]不用

G为图,n为图的节点数目,
s,t分别为源点和终点;
Path用来记录最短路径;
返回值为s,t之间的最短路径长度;

================================================}

{!!! 注意:输入的图的权必须非负}

function Dijkstra(var G:adj_matrix;n:integer;s,t:integer;var Path:array of integer):integer;
var
d:array[1..MaxSize]of integer; {用来纪录从源点到所有点的最短路径的长度}
mark:array[1..MaxSize]of 0..1; {用来标记已经找到最短路径的顶点,0表示已经找到最短路径,1表示还未找到最短路径}
i,j,w,min:integer;
begin
FillChar(mark,SizeOf(mark),0);
mark[s]:=1;
for i:=1 to n do {这里规定s,i不相邻时G[i,j]=∞}
begin
d[i]:=G[s,i];
Path[i]:=s;
end;
Path[s]:=0;
d[s]:=0;
for i:=1 to n-1 do
begin
min:=maxint;
w:=0;
for j:=1 to n do {找到所有还没有找出最短路径的节点中到s最近的节点w}
if (mark[j]=0)and(min>=d[j]) then
begin
min:=d[j];
w:=j;
end;
mark[w]:=1;
for j:=1 to n do {修改所有还没有找出最短路径的节点到s的距离}
if (mark[j]=0)and(G[w,j]<>maxint)and(d[j]>d[w]+G[w,j]) then
begin
d[j]:=d[w]+G[w,j];
Path[j]:=w;
end;
end;
result:=d[t];
end;




{==================================

单源最短路径

Bellman-Ford 算法

适用条件:所有情况,边权可以为负

===================================}


function Bellman_Ford(var G:adj_matrix;size:integer;s,t:integer;var path:array of integer;var success:boolean):integer;

{G为图,size为图的节点数目,s,t分别为源点和终点,
success用来标示该函数是否成功,如果存在一个从源点可达的权为负的回路则success=false;
返回值为s,t之间的最短路径长度;}

var
d:array of integer; {用来纪录从源点到所有点的最短路径的长度}
i,j,k:integer;

begin
setlength(d,size+1);
for i:=1 to size do
begin
d[i]:=maxint div 2; {注意:不可以将无穷大取maxint,否则会溢出;取maxint div 2就可以了}
path[i]:=0;
end;
d[s]:=0;
for k:=1 to size-1 do
for i:=1 to size do
for j:=1 to size do
if (d[j]>d[i]+G[i,j]) then {注意,这里i若和j不相邻,则必须规定G[i,j]=∞,但是不可以将无穷大取maxint,否则会溢出;取maxint div 2就可以了}
begin
d[j]:=d[i]+G[i,j];
path[j]:=i;
end;

for i:=1 to size do
for j:=1 to size do
if (d[j]>d[i]+G[i,j]) then
begin {存在一个从源点可达的权为负的回路}
success:=false;
result:=0;
exit;
end;
result:=d[t];
success:=true;
end;

{==========================================

最小生成树

Prim 算法

基本思想:

T为最小生成树的边集;
U为一个顶点集
V为图的顶点集
图的顶点标号为1..n

procedure Prim;
begin
T:=Φ;
U:={1};
while U<>V do
begin
(u,v):= u∈U且v∈V-U的最小权边;
T:=T∪{(u,v)};
U:=U∪{v};
end;
end;

======================}


procedure Prim(var G:adj_matrix;size:integer);{G为图,size为图的节点数目;注意:假设输入的图是连通图}
var
lowcost:array [1..maxsize] of integer;
used:array [1..maxsize] of boolean;
closeset:array[1..maxsize] of integer;
i,j,k,min:integer;

begin
for i:=2 to size do {初始化,此时U只含有顶点1}
begin
lowcost[i]:= G[1,i];
closeset[i]:=1;
used[i]:=false;
end;
used[1]:=true;
for i:=2 to size do
begin
min:=maxint;
j:=i;
for k:=2 to size do {用选择法寻找顶点分别在V-U与U中权最小的边}
if (not used[k])and(lowcost[k]<min) then
begin
min:=lowcost[k];
j:=k;
end;
writeln(fout,'(',closeset[j],',',j,')'); {输出找到的最小生成树的一条边,此处可根据情况修改}
used[j]:=true; {将j填加到U}
for k:=2 to size do {调整lowcost和closeset}
if (not used[k])and(G[j,k]<lowcost[k]) then
begin
lowcost[k]:=G[j,k];
closeset[k]:=j;
end;
end;
end;



{==========================================

每对节点间最短路径
Flod-Warshall 算法

===========================================}


procedure Flod_Warshall(var G:adj_matrix;size:integer;var D,P:adj_matrix);
{D[i,j]表示从i到j的最短距离;P[i,j]表示从i到j的最短路径上j 的父节点}
var
i,j,k:integer;
begin
{初始化}
for i:=1 to size do {这里假设i,j不相邻时G[i,j]=∞。注意:为了防止溢出,无穷大不能取maxint,可以取maxint div 2}
for j:=1 to size do
begin
D[i,j]:=G[i,j];
P[i,j]:=i; {P[i,j]=0表示不存在从i到j的路径}
end;
for i:=1 to size do
begin
D[i,i]:=0;
P[i,i]:=0;
end;
for k:=1 to size do {进行size次迭代}
for i:=1 to size do
for j:=1 to size do
if (d[i,j]>d[i,k]+d[k,j]) then
begin
d[i,j]:=d[i,k]+d[k,j];
p[i,j]:=p[k,j];
end;
end;



{==========================================

求有向图的传递闭包

G[i,j]=1表示i,j相邻;G[i,j]=0表示i,j不相邻

===========================================}


procedure Transtive_Closure(var G:adj_matrix;size:integer;var T:TranstiveClosureType);
var
i,j,k:integer;
begin
for i:=1 to size do
for j:=1 to size do
if G[i,j]<>0 then //若i与j不相邻则G[i,j]=0,此处可根据情况修改
T[i,j]:=true;
for i:=1 to size do T[i,i]:=true;
for k:=1 to size do
for i:=1 to size do
for j:=1 to size do
T[i,j]:=T[i,j] or (T[i,k] and T[k,j]);
end;



{======================================================================
深度优先搜索,找出关于图的结构的信息

存储结构信息的变量说明如下:

/ =0 顶点i到顶点j无边
G-邻接矩阵表示的图,g[i,j]= -| >0 边(i,j)存在且允许访问
\ <0 边(i,j)存在但不允许访问

/ 0 顶点i到j没有边
| 1 (i,j)为树枝边
EdgeType - 边的类型,EdgeType[i,j]=| 2 (i,j)为反向边
| 3 (i,j)为正向边
\ 4 (i,j)为交叉边

d[u] - 第一次访问顶点u时给u盖上的时间戳
f[u] - 当访问完了以u为根的子树上的所有顶点后给u盖上的时间戳

/ 0 白色,未被访问过的节点标白色
c - 顶点颜色表 c[u] = | -1 灰色,已经被访问过一次的节点标灰色
\ 1 黑色,当该节点的所有后代都被访问过标黑色

p - 顶点的前驱表 p[u] - u在深度优先树中的父节点
b - 顶点的度数表 b[u] - 深度优先树中顶点u的度数
l - 顶点的low值表,
l[u]用来纪录顶点u或u的后代所能追溯到的最早(最先被访问)的祖先节点v的d[v]值
/ d[u] u首次被访问
l[u]= | min(l[u],d[v]) 访问反向边(u,v)时
\ min(l[u],l[s]) u的儿子s的关联边全部被访问时


边的分类方法:
1。树枝:
深度优先森林中的边。如果V是在访问边(u,v)时第一次被发现(此时v是白色的),那么
(u,v)是一个树枝。在树枝边中,我们称u为v的父母,既作p[v]=u;
2。反向边:
在深度优先树种连接顶点u到他的祖先的那些非树枝边。环也被认为是反向边。显然,
如果第一次访问(u,v)时v为灰色,则(u,v)为反向边。在对图的深度优先搜索中没有发现
反向边,则该图没有回路。
3。正向边
深度优先树中连接顶点u到他的祖先的那些非树枝边。当第一次访问(u,v)时,v为黑色
且d[u]<d[v],则(u,v)为正向边。
4。交叉边
其它类型的边。他们既可以连接一棵深度优先树中的两个顶点,只要一顶点不是另一
个顶点的祖先,也可以连接分属两棵深度有线数的顶点。当第一次访问(u,v)时,v为黑色
且d[u]>d[v],则(u,v)为交叉边。

*深度优先搜索的复杂度为 θ(V+E)

*有向图或无向图G的深度优先森林中,节点v是节点u的后裔当且仅当 d[u]<d[v]<f[v]<f[u]。

*找出有向图的强连通分支的算法:
S1. 调用DFS(G)以计算出每个节点u的完成时刻f[u];
S2. 计算出G的转置G',其中G'是将G的每一条边反向后得到;
S3. 调用DFS(G'),但在DFS的主循环里按照f[u]递减的顺序考虑各节点;
S4. 输出S3中产生的深度优先森林中每棵树的节点,作为各自独立的强连通分支。

*根据f[v]从大到小对顶点排序,即得到有向图的拓扑排序;

*在对无向图G进行深度优先搜索时,G只有树枝和反向边两种边。

*若无向图的树枝边(u,v)满足l[v]>d[u],即从v及其后代不存在一条连接u的祖先的反向边,
则(u,v)为桥;

* 无向图的节点v是割点当且仅当v具备以下条件成立:
若v是深度优先树的根(p[v]=0),则v的子节点数目超过1;
若v不是深度优先树的根(p[v]>0),则v存在一个子节点w,满足l[w]>=d[v]。

}


{==========================================
!!!!IMPORTANT
对于有向图,请把前面有!!!的行删去;
对于权可以为负的图,请修改前面有!!!的行。

============================================}
procedure DFS(var G:adj_matrix;size:integer);
const
white=0;
gray=-1;
black=1;
var
EdgeType:adj_matrix;
d,f,c,p,b,l:array[1..MaxSize] of integer;
u,time:integer;

procedure DFS_visit(u:integer);
var
v:integer;
begin
c[u]:=gray;
inc(time);
d[u]:=time;
l[u]:=time;
for v:=1 to size do
if G[u,v]>0 then {G[u,v]>0表示边(u,v)存在且未被访问,当边(u,v)不存在时G[u,v]=0}
case c[v] of
white: begin
{!!!} {G[u,v]:=-G[u,v]; {设置(u,v)暂时不可访问的标志,对于有向图,请把此行删去}
EdgeType[u,v]:=1; {(u,v)为树枝边}
inc(b[u]); {深度优先树中顶点u的度数加1}
p[v]:=u; {深度优先树中顶点v的父亲是u}
DFS_visit(v); {深度优先搜索顶点v}
if l[v]<l[u] then l[u]:=l[v]; {u的儿子v的关联边全部被访问时,l[u]=min(l[u],l[v])}
{!!!} {G[u,v]:=-G[u,v]; {恢复(u,v)可访问,对于有向图,请把此行删去}
end;
gray: begin
EdgeType[u,v]:=2; {(u,v)为反向边}
if d[v]<l[u] then l[u]:=d[v]; {访问反向边(u,v)时,l[u]:=min(l[u],d[v])}
end;
black: begin
if d[v]>d[u] then EdgeType[u,v]:=3 {(u,v)为正向边}
else EdgeType[u,v]:=4; {(u,v)为交叉边}
end;
end; {end of case}
c[u]:=black;
inc(time);
f[u]:=time;
end;



{===============================================

最大流算法

基于Ford-Fulkerson 算法的 Edmonds-Karp 实现

求出以s为源,以t为汇的最大网络流

时间复杂度为O(VE^2)

C为容量网络,如果(u,v)没有边,规定c[u,v]=0;

注意,Flow是一个已经求出的可行流,一般可以取Flow[i,j]=0;

================================================}


procedure Edmonds_Karp(var C:adj_matrix;size:integer;s,t:integer;var Flow:adj_matrix);
var
L:array [1..maxsize,1..2] of integer; {记录标号}
Q:array [1..maxsize] of integer; {队列}
u,v,head,tail:integer;

begin
repeat
fillchar(L,sizeof(L),0); {初始化,所有点标号为0}
L[s,1]:=0;
L[s,2]:=maxint;
head:=0;
tail:=1;
Q[tail]:=s; {s入队}
while (head<tail)and(L[t,2]=0) do {当Q非空并且汇点未标号}
begin
inc(head);
u:=Q[head];
for v:=1 to size do
if (Flow[u,v]<C[u,v])and(L[v,2]=0) then {如果v没有标号且(u,v)的流量可改进}
begin
inc(tail);
Q[tail]:=v;
L[v,2]:=min(C[u,v]-Flow[u,v],L[u,2]); {L[v,2]记录下从s到v的增广路径中最小的可改进流量}
L[v,1]:=u; {L[v,1]记录下从s到v的增广路径中v的前驱}
end;
end; {while}
if L[t,2]>0 then {如果汇点已标号}
begin
v:=t;
u:=L[v,1];
while v<>s do
begin
Flow[u,v]:=Flow[u,v]+L[t,2];
Flow[v,u]:=-Flow[u,v];
v:=u;
u:=L[v,1];
end; {while}
end; {then}
until L[t,2]=0; {直到汇点未标号}
end;

{=================================================

容量有上下界的最大流问题

B为容量下界矩阵;
C为容量上界矩阵;
如果存在可行流,返回值为真

===================================================}

function Max_Flow_With_Limt(var B,C:adj_matrix;size:integer;s,t:integer;var Flow:adj_matrix):boolean;
var
new_c:adj_matrix; {新的容量网络}
i,j:integer;
begin
fillchar(new_c,sizeof(new_c),0);
{构造一个新的容量网络new_c}
for i:=1 to size do {增加一个新汇点size+2}
for j:=1 to size do
new_c[i,size+2]:=new_c[i,size+2]+B[i,j];
for i:=1 to size do {增加一个新源点size+1}
for j:=1 to size do
new_c[size+1,i]:=new_c[size+1,i]+B[j,i];
for i:=1 to size do
for j:=1 to size do
new_c[i,j]:=C[i,j]-B[i,j];
new_c[s,t]:=maxint;
new_c[t,s]:=maxint;
{用Edmonds_Karp算法在新的容量网络中求最大流}
fillchar(flow,sizeof(flow),0);
Edmonds_Karp(new_c,size+2,size+1,size+2,flow);
{检验原容量限制网络是否有可行流}
for i:=1 to size do
if flow[size+1,i]<new_c[size+1,i] then
begin
result:=false;
exit;
end;
result:=true;
{将新容量网络中求出的可行流化为原来的容量网络中的可行流}
flow[s,t]:=C[s,t];
flow[t,s]:=-flow[s,t];

for i:=1 to size do
for j:=1 to size do
if flow[i,j]>=0 then
begin
flow[i,j]:=flow[i,j]+B[i,j];
flow[j,i]:=-flow[i,j];
end;
{用Edmonds_Karp将求出的可行流进行放大}
Edmonds_Karp(c,size,s,t,flow);
end;
starfish 2001-08-26
  • 打赏
  • 举报
回复
寻找最大流的基本方法是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表示可改进路还可以延伸,还需要重新搜索网络中的顶点;否则说明最小费用的可改进路已经找到或者最大流已经求出。

下面是算法的伪代码:

cost ← 0;
repeat
可改进路撤空;
设源点的CT值为0并进入可改进路;
repeat
break ← false;
for u ←1 to N do
begin
分析U出发的所有弧<U,V>;
if (<U,V>的流量可改进)and(源点至U有通路)and(U的CT值+<U,V>的费用 < V的CT值) then
begin
break ← true;
V的CT值 ← U的CT值+<U,V>的费用;
V进入可改进路经并为之标号;
end if
end for
until break=false
if 汇点已标号 then
begin
从汇点出发倒向修正可改进路的流量;
cost ← cost + ∑B(e)*d(其中e∈P,d为P的可改进量);
end if
until 汇点未标号;

可见,上述的算法和求最大流的Edmonds-Karp标号算法几乎一样,因为这两种算法都使用宽度优先搜索来来寻找增广路径,所以复杂度也相同,都是O(VE),其中V是节点数目,E是边数目。

以前我写过最小费用流的程序的,但是现在找不到了,硬盘上太混乱了:)

starfish 2001-08-26
  • 打赏
  • 举报
回复
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的元素数目一定是最少的。

Zig 2001-08-26
  • 打赏
  • 举报
回复
你要什么啊?用线性规划解嘛。:PP
dongmen 2001-08-26
  • 打赏
  • 举报
回复
网络流n个问题你要哪个?(我只是帮大家问,不然只怕没人管)

33,028

社区成员

发帖
与我相关
我的任务
社区描述
数据结构与算法相关内容讨论专区
社区管理员
  • 数据结构与算法社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧