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是节点的数量,s和t分别是起始点和汇点的节点编号
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]==0,flow[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就是最后求出的最大流的大小
}