经典算法 | ford-fulkerson算法和edmonds-karp算法

来源:互联网 发布:三菱plc编程软件免费下载 编辑:程序博客网 时间:2024/05/20 12:25

Ford-fulkerson算法基本步骤:

1)       置初始可行流

2)       构造原网络的残流网络,在残流网络中找s-t的有向路径。(s为起点,t为终点),如果没有,算法得到最大流,结束算法,否者继续下一步。

3)       根据残流网络中的s-t有向路径写出对应到原网络中的s-t增广路径。对于增广路径中的前向弧,置s(e) = u(e) – f(e)。对于反向弧,置s(e)=f(e)

4)       计算crement=min{s(e1),s(e2),…s(ek)}

5)       对于增广路中的前向弧,令f(e)=f(e)+crement;对于其中的反向弧,令f(e)=f(e)-crement;转步骤2)

//残流网络的定义:给定一个流网络G以及其上的一个流flow,网络G关于flow的残流网络G*与G有相同的顶点集V,而网络G中的每一条边,对应于G*中的一条边或两条边,设(v,w)是G的一条边。当flow(v,w)> 0的时候,(w,v)是G*中的一条边,该边的容量为cap*(w,v) = flow(v,w);当flow(v,w)<cap(v,w)时,(v,w)是G*中的一条边,该边的容量为cap*(v,w) = cap(v,w) – flow(v,w)。

//残流网络是一个有向图,因此,当存在从s到t的路径的时候,当前流还可以继续增大,当不存在这样的路径的时候,当前流就是当前网络的最大流。

//当不存在最大流的时候当前流是当前图的最大流的证明过程:、

要想证明这一点,首先要知道一个定理:

定理26.5:流网络G中任意流f的值不能超过G的任意切割的容量,

这里任意切割都有两个容量,一个是从s到t的容量,一个是从t到s的容量,而流f的值这两个容量都不能超过

 

定理26.6(最大流最小切割定理)

设f是流网络G=(V,E)中的一个流,源节点为s,汇节点为t,则一下条件是等价的:

1.   f是G的一个最大流

2.   G的残流网络不包含任何增广路径、

3.   |f|=c(S,T),其中(S,T)是流网络G的某个切割

当1成立的时候,2一定成立,因为当f时一个最大流的时候,假如还存在增广路径的话,那说明当前流还是可以增大的,也就是说当前流不是最大流

现在证明2->3


证明完毕。、

Edmonds-Karp的改进算法:

Edmonds-karp虽然说是改进算法,但是步骤和Ford-fulkerson算法的步骤是完全一样的,只是在残流网络中找增广路径的时候使用的是广度优先遍历,因此可以找到最短可增流路径,

edmonds-karp算法实现:

#include <cstdio>

#include <queue>

#include <algorithm>

Using namespace std;

template<class T>

class Graph{

private :

  int v,s,t,prev[N]; //v是节点的数量,st分别是起始点和汇点的节点编号

T cap[N][N],flow[N][N];

Bool bfs();

Public :

  Voidclear();

void setMaxNode(int n) {v=n};

void insert(int nu,int nv,int c){cap[nu][nv]+=c;}//这条简单来说就是在原图上添加一条边,边的容量是c,假如这条边存在,则把它的容量加c

T getFlow(int nu,int nv) { returnflow[nu][nv]; }

T maxFlow (int ,int);

};

 

template<class T>

void Graph<T> :: clear(){

v = 0;

for(int i=0;i<N;i++)

 for(int j=0;j<N;j++)

  cap[i][j]=T(0);

}//这里是模板类的函数,这个函数的功能是把流网络中的所有边的容量置为0

template<class T>

bool Graph<T> :: bfs(){

queue<int> Q;

bool vst[N]={false};//用来判断在广度优先遍历的过程中某个节点是否访问过

Q.push(s); vst[s]=true;

while(!Q.empty()) {

  int u=Q.front(); Q.pop();

  for(int i=0;i<v;i++)

   if(!vst[i]&&cap[u][i]!=flow[u][i]) //这一条成立的先决条件是原图是有向图,且其任意两点间自由一条边相连,假如有两条或者是多条容量边相连,则需要增加新的节点,把原图转换成每个节点只有一条容量边相连的情况,!vst[i]表示该节点没有被访问过,cap[u][i]!=flow[u][i]表示在该图对应的残流网络中,假如cap[u][i]!=0,则cap[i][u]==0,cap为正方向的时候,cap[u][i]!=flow[u][i]表示流量还没有达到cap容量的上限,当cap为反方向的时候,cap[u][i]==0flow[u][i]为负数,cap[u][i]!=flow[u][i]表示该边相反方向存在流,因此流网络在残流网络中该条边上存在边

{

    Q.push(i); prev[i]=u; vst[i]=true;

     if(i==t) return true;

}

}

Return false;

}

template<class T>

T Graph<T> :: maxFlow(int ss,inttt){

S=ss; t=tt;

for(int i=0;i<v;i++)

 for(int j=0;j<v;j++)

  flow[i][j]=T(0); //将流初始化为0

T f=0;

while(bfs()) {

   T ex=T(INF);//ex初始化为无限大

   For(int c=t;c!=s;c=prev[c])ex<?=cap[prev[c]][c]-flow[prev[c]][c];//ex为残流网络中数值最大的边,之所以是cap[prev[c]][c]-flow[prev[c]][c]是因为当cap为正方向时,残流网络中边的值为cap[prev[c]][c]-flow[prev[c]][c],当cap为负方向是,cap[prev[c]][c]==0,   flow[prev[c]][c]为负值,这两个数相减得值就是流的值

For(intc=t;c!=s;c=prev[c])

{ flow[prev[c][c] +=ex; flow[c][prev[c]]-=ex;}

f+=ex;

}

Return f;//这里的f就是最后求出的最大流的大小

}