第26章:最大流

一:流网络

流网络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,要求 0f(u,v)c(u,v)

2:流量守恒:对于属于集合V-{s,t}的所有结点u来说,要求 vVf(v,u)=vVf(u,v) 。当 (u,v)E 时,从结点u到结点v之间没有流,因此f(u,v)=0;

我们称非负数值f(u,v)为从结点u到结点v的流。一个流f的值|f|定义如下:

|f|=vVf(s,v)vVf(v,s)

也就是说,流f的值是从源结点流出的总流量减去流入源结点的总流量。在最大流问题中,给定一个流网络G,一个源结点s和一个汇点t,我们希望找到值最大的一个流。

二:Ford-Fulkerson方法

Ford-Fulkerson方法循环增加流的值。在开始的时候,对于所有的结点 u,vV , 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;
                }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值