一:流网络
流网络G=(V,E)是一个有向图,图中每条边(u,v)有一个非负的容量值 c(u,v)≥0 。而且如果边集合E包含一条边(u,v),则图中不存在反方向的边(v,u)。如果边(u,v)不属于E,则定义c(u,v)=0,并且图中不允许自循环。在流网络的所有结点中,我们特别分别出两个特殊结点,源结点s和汇点t,为方便起见,假定每个结点都在从源点到汇点的某条路径上。
我们现在可以给出流的形式化定义。假设G=(V,E)为一个流网络,其容量函数为c。假设s为源结点,t为汇点。图中任意边(u,v)的流是一个实值f(u,v),其满足下面两个性质
1:容量限制:对于所有的结点u和v,要求 0≤f(u,v)≤c(u,v) 。
2:流量守恒:对于属于集合V-{s,t}的所有结点u来说,要求 ∑v∈Vf(v,u)=∑v∈Vf(u,v) 。当 (u,v)∉E 时,从结点u到结点v之间没有流,因此f(u,v)=0;
我们称非负数值f(u,v)为从结点u到结点v的流。一个流f的值|f|定义如下:
也就是说,流f的值是从源结点流出的总流量减去流入源结点的总流量。在最大流问题中,给定一个流网络G,一个源结点s和一个汇点t,我们希望找到值最大的一个流。
二:Ford-Fulkerson方法
Ford-Fulkerson方法循环增加流的值。在开始的时候,对于所有的结点 u,v∈V , f(u,v)=0,给出的初始流值为0。在每一次迭代中,我们将图G的流值进行增加,方法就是在一个关联的“残存网络” Gf 中寻找一条“增广路径”。一旦知道残存网络中一条增广路径的边,就可以很容易辨别出G中的一些具体的边,我们可以对这些边上的流量进行修改,从而增加流的值。虽然Ford-Fulkerson方法的每次迭代都增加流的值,但是对于图G的一条特定边来说,其流量可能增加,也可能减少;对某些边的流进行缩减可能是必要的,以便让算法可以将更多的流从源结点发送到汇点。重复对流进行这一过程,直到残存网络中不再存在增广路径为止,这时候会获得一个最大流。
在残存网络中寻找增广路径的方法有很多种,下面的代码是用广度优先搜索寻找增广路径。代码如下:
//求从源点source到汇点sink的最大流量
double ford_fulkerson_bfs(const vector<list<int>>& graph,const vector<vector<double>>& capacity,int source,int sink)
{
vector<list<int>> residual_network=graph; //residual_network表示残余网络;
vector<vector<double>> c=capacity; //c表示残余网络每条边对应的容量;
double max_flow=0; //从源点source到汇点sink的最大流量;
double flow=0;
vector<int> pred=bfs(residual_network,source); //表示用广度优先搜索方法得到的残余网络中从源>结点s到汇点t的简单路径,也就是残余网络中增广路径;
const int NIL=-1; //NIL表示不存在的顶点;
while(pred[sink]!=NIL){
flow=residual_capacity(pred,c,sink); //返回增广路径中的残存容量;
max_flow+=flow;
int vertex=sink;
while(pred[vertex]!=-1){
int last_vertex=pred[vertex];
if(flow<c[last_vertex][vertex])
c[last_vertex][vertex]-=flow;
else{
c[last_vertex][vertex]=0;
remove_edge(residual_network,last_vertex,vertex);
}
if(c[vertex][last_vertex]!=0){
c[vertex][last_vertex]+=flow;
}
else{
add_edge(residual_network,vertex,last_vertex);
c[vertex][last_vertex]=flow;
}
vertex=last_vertex;
}
pred=bfs(residual_network,source);
}
print_edge_flow_value(graph,c); //输出从源点到汇点的最大流值在图中每条边的分配;
return max_flow;
}
//上述代码中的residual_capacity(),add_edge(),remove_edge(),print_edge_flow_value()代码如下:
//残余网络的一条增广路径能够为每条边增加的流量的最大值,也就是增广路径所有边中容量的最小值;
double residual_capacity(const vector<int>& pred,const vector<vector<double>>& capacity,int sink)
{
double flow=DBL_MAX;
int vertex=sink;
const int NIL=-1; //NIL表示不存在的顶点;
while(pred[vertex]!=NIL){
int last_vertex=pred[vertex];
if(capacity[last_vertex][vertex]<flow)
flow=capacity[last_vertex][vertex];
vertex=last_vertex;
}
return flow;
}
void remove_edge(vector<list<int>>& graph,int u,int v)
{
auto iter=find(graph[u].begin(),graph[u].end(),v);
graph[u].erase(iter);
}
void add_edge(vector<list<int>>& graph,int u,int v)
{
graph[u].push_back(v);
}
void print_edge_flow_value(const vector<list<int>>& graph,const vector<vector<double>>& residual_network_capacity)
{
for(int u=0;u!=graph.size();++u)
for(auto iter=graph[u].begin();iter!=graph[u].end();++iter)
{
int v=*iter;
cout<<"edge( "<<u<<", "<<v<<") flow value: "<<residual_network_capacity[v][u]<<endl;
}
}