一、概念介绍
1、流网络
流网络是一个有向图G=(V,E),图中的每条边(u,v)有一个非负的容量值,表示边(u,v)上可以流进的最大流量。图中有两个特殊的节点:源节点s和汇聚节点t。
流网络有以下几个性质:
容量守恒:流经边(u,v)的流量不能大于(u,v)的容量
流量守恒:除了s和t外,对于其它所有节点都有,流入该节点的流量等于流出该节点的流量
2、最大流
在一个给定的流网络G中,s到t能够通过的最大流量。
3、残存网络
给定一个流网络G和一个流,流的残留网络Gf拥有与原来网络相同的节点。流网络中每条边将对应残留网络中的一条或者两条边,对于流网络中的任意边(u,v),流量为f(u,v),容量为c(u,v):
●如果f(u,v)>0,则在残存网络中包含一条容量为f(u,v)的边(v,u)
●如果f(u,v)<c(u,v),则残存网络中包含一条容量为c(u,v)-f(u,v)的边(u,v)
4、增广路径
已知一个流网络和一个流f,增广路径p是其残存网络Gf上s到t的一条简单路径
二、Ford-Fulkerson方法
Ford-Fulkerson方法的描述:
将残存网络初始化为原网络
while(残存网络中是否还能找到一条增广路径p)
cf=增广路径p上值最小的边的值
更新p上所有的边:(若边(u,v)在p上,则边(u,v)的值-cf,反向边(v,u)的值+cf)
由上述分析可知用Ford-Fulkerson方法求最大流的关键在于寻找残存网络中的增广路径,下面介绍两种算法
1、Edmonds-Karp算法
该算法采用bfs寻找残存网络中的增广路径
Edmonds-karp算法jva模板
int s;//源节点
int t;//汇聚节点
int n;//节点总个数
int[][] graph;//网络图,graph[i][j]=c,表示边(i,j)的容量为c,graph[i][j]==0,表示边(i,j)不存在
/**
* 判断残余网络rGraph中是否还有增广路径,并将增广路径记录在path中
* @param rGraph
* @param path
* @return
*/
boolean hasPath(int[][] rGraph,int[] path)
{
int[] vis=new int[n];
Queue<Integer> q=new LinkedList<Integer>();
q.add(s);
vis[s]=1;
while(!q.isEmpty())
{
int top=q.poll();
for(int i=0;i<n;i++)
{
if(vis[i]==0 && rGraph[top][i]>0)
{
q.add(i);
vis[i]=1;
path[i]=top;
if(i==t) return true;
}
}
}
return false;
}
int ek()
{
int max=0;
int path[]=new int[n];//纪录增广路径,path[u]=v表示u的前驱节点是v
int rGraph[][]=new int[n][n];//残存网络
//*//将残存网络初始化为原网络
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
{
rGraph[i][j]=graph[i][j];
}
}
//*//
while(hasPath(rGraph, path))
{
int min=0xffff;
//**//找到增广路径上的最小边
for(int v=t;v!=s;v=path[v])
{
int u=path[v];
min=Math.min(min, rGraph[u][v]);
}
//**//
//***//更新增广路径上的边和其反向边
for(int v=t;v!=s;v=path[v])
{
int u=path[v];
rGraph[u][v]-=min;
rGraph[v][u]+=min;
}
//***//
max+=min;
}
return max;
}
2、Dinic算法
算法流程
Dinic算法java模板
int s;//源节点
int t;//汇聚节点
int n;//节点的总个数
int dis[];//dis[i]=k:表示节点i属于第k层,dis[i]=-1,表示结点i没出现在层次图中
int graph[][];//网络图,graph[u][v]=c,表示边(u,v)的容量为c,graph[u][v]=0,表示边(u,v)不存在
int rGraph[][];//残存网络
/**
* bfs()建立层次网络
*/
boolean bfs()
{
int vis[]=new int[n];
Arrays.fill(dis, -1);
Queue<Integer> q=new LinkedList<Integer>();
q.add(s);
vis[s]=1;
dis[s]=0;
while (!q.isEmpty())
{
int top=q.poll();
for(int i=0;i<n;i++)
{
if(vis[i]==0 && rGraph[top][i]!=0)
{
dis[i]=dis[top]+1;
q.add(i);
}
}
}
if(dis[t]>0) return true;
else return false;
}
/**
* 寻找增广路径
* @param x
* @param min
* @return 0表示没找到增广路径
*/
int dfs(int x,int min)
{
int a=0;
if(x==t) return min;
for(int i=0;i<n;i++)
{
if(rGraph[x][i]>0 && dis[i]==dis[x]+1 && (a=dfs(i, Math.min(min, rGraph[x][i])))!=0)
{
rGraph[x][i]-=a;
rGraph[i][x]+=a;
return a;
}
}
return 0;
}
int dinic()
{
rGraph=graph;//初始化残存网络
int max=0;
int a=0;
while(bfs())//bfs计算层次图
{
while((a=dfs(s,0xffff))!=0)//寻找增广路径
{
max+=a;
}
}
return a;
}