最大网络流之Ford-Fulkerson算法和ScalingFord-Fulkerson算法

1.最大网络流问题介绍

这里写图片描述
我们要解决的问题就是怎样分配,使得从 s 流出的货物到城市 s 的黄金最多,并且图中相邻城市之间实际运输的黄金数量不能超过他们的容量c.
当然我们可以不加思索的用我们的超级武器$GLPK$来解决此问题,但这个问题比一般的线性规划问题更特殊,图的很多性质我们可以派上用场,关于图的知识可参见算法导论。下面我们介绍非常一个非常有套路的算法和它的改进版本。

2.Ford-Fulkerson算法

1956年,Lester Randolph Ford Jr. and Delbert Ray Fulkerson提出了Ford-Fulkerson算法。
Ford-Fulkerson算法原理如下:
Let p be a simple s − t path in residual graph Gf, called augmentation path. We define bottleneck(p, f) as the minimum capacity of edges in path p.

Ford-Fulkerson algorithm:
1: Initialize f(e) = 0 for all e.
2: while there is an s − t path in residual graph Gf do
3: arbitrarily choose an s − t path p in Gf;
4: f =augment(p, f);
5: end while
6: return f;

该算法的最金典之处在于每当一条边上从起点节点向端点节点运输$f(u,v)$黄金时,则增加一条方向边$v->u$,其运输容量为$f(u,v)$.具体如下图所示
这里写图片描述

2.1Ford-Fulkerson算法具体应用

问题如下图,求从城市0到城市1的最大流
这里写图片描述
我们跑Ford-Fulkerson算法最后得到的流图如下:
这里写图片描述
具体程序如下:

/***********************************************************
Description:FordFulkerson算法求解网络最大流
Author:Robert.TY
Date:2016.11.30 
************************************************************/
#include<iostream>
#include<algorithm>
#include<queue>
#include<iomanip>
#include<limits.h>
using namespace std;
class Max_Network_Flow{
public:
    int V ;
    int graph[6][6];
    int max_flow;
    //构造函数 
    Max_Network_Flow(int tempV,int tempgraph[][6]){
        V=tempV;
        cout<<"V="<<V<<endl;
        for(int i=0;i<6;i++){
            for(int j=0;j<6;j++){
                graph[i][j]=tempgraph[i][j];
            }
        }
    }
    int FordFulkersonMaxFlow(int s, int t);//FordFulkerson算法求解最大流 
    bool PathSearch( int s, int t, int path[6]);//路径查询
    int Augment(int path[6],int s,int t);// 增加流操作 
    void ShowResult(){
        max_flow=FordFulkersonMaxFlow( 0, 1);
        cout<<"max_flow="<<max_flow<<endl;
    }
};
int main(){
    int V = 6;
    int graph[6][6] = {   { 0, 0, 10, 10, 0, 0 }, 
                          { 0, 0, 0, 0, 0, 0 },
                          { 0, 0, 0, 2, 4, 8 },
                          { 0, 0, 0, 0, 0, 9 },
                          { 0, 10, 0, 0, 0, 0 },
                          { 0, 10, 0, 0, 6, 0 } };

    cout<<"输出原始网络流图的邻接矩阵:"<<endl; 
    for(int i=0;i<6;i++){
        for(int j=0;j<6;j++){
            cout<<setw(5)<<graph[i][j]<<"  ";
        }
        cout<<endl;
    }
    cout<<"输出完毕:"<<endl;
    Max_Network_Flow graph1(V,graph);
    graph1.ShowResult() ;

    return 1;
}
int Max_Network_Flow::FordFulkersonMaxFlow(int s, int t){


    int maxFlow = 0;
    int path[6];
    while(PathSearch( s, t, path)==1){//查询一条可行路径 
        maxFlow+=Augment(path,s,t);//再该路径进行增流操作 
    }
    cout<<"最后网络流的邻接矩阵:"<<endl;
    for(int i=0;i<6;i++){
        for(int j=0;j<6;j++){
            cout<<setw(5)<<graph[i][j]<<" ";
        }
        cout<<endl;
    }
    return maxFlow;
} 
bool Max_Network_Flow::PathSearch( int s, int t, int path[6]) {
        bool visited[6] ;
        queue<int> queue1;
        queue1.push(s);
        visited[s] = true;
        //标准的BFS算法
        while(queue1.size() > 0){
            int top = queue1.front();//返回队列头部的元素, 
            queue1.pop();//出队操作 
            for(int i=0; i<V; i++){
                if(!visited[i] && graph[top][i] > 0){//当节点i没有被访问,没有在队列当中且是top节点的邻居时
                    queue1.push(i);
                    visited[i] = true;
                    path[i] = top;
                }

            }
        }
        return visited[t] == true;
    }
int Max_Network_Flow:: Augment(int path[6],int s,int t){
    int min_flow =INT_MAX;
    //找到当前路径中最小的流量 bottleneck
    for(int v=t; v != s; v=path[v]){
        int u = path[v];
        min_flow=min_flow>graph[u][v]?graph[u][v]:min_flow;
    }
    //cout<<"min_flow="<<min_flow<<endl;
    cout<<endl;
    //更新路径中的每条边反向边的权值增加min_flow,正向边的权值减少min_flow
    for(int v=t; v != s; v=path[v]){
        int u = path[v];
        graph[u][v] -= min_flow;
        graph[v][u] += min_flow;
    }
    return min_flow;
}

求解结果如下:
这里写图片描述
Ford-Fulkerson算法时间复杂度分析:
这里写图片描述

2.3Ford-Fulkerson算法总结

1:In the analysis of Ford-Fulkerson algorithm, the integer restriction of capacities is important: the bottleneck edge leads to an increase of at least 1.
2:The analysis doesn’t hold if the capacities can be irrational. In fact, the flow might be increased by a smaller and smaller number and the iteration will be endless.
3:Worse yet, this endless iteration might not converge to the
maximum flow.

3.ScalingFord-Fulkerson算法

针对Ford-Fulkerson算法, Uri Zwick提出改进,即ScalingFord-Fulkerson算法。
下面给出一个例子说明Ford-Fulkerson算法的缺点:
这里写图片描述

当我们跑Ford-Fulkerson算法时,很有可能是出现这样的糟糕情况,如下:
第一步:
这里写图片描述
第二步:
这里写图片描述
第三步:
这里写图片描述

我们提出改进技术:
这里写图片描述
Scaling Ford-Fulkerson algorithm原理如下:

Scaling Ford-Fulkerson algorithm:
1: Initialize f(e) = 0 for all e.
2: Let ∆ = C;
3: while ∆ ≥ 1 do
4: while there is an s − t path in Gf(∆) do
5: choose an s − t path p;
6: f =augment(p, f);
7: end while
8: ∆ = ∆/2;
9: end while
10: return f;

Intuition: flow is augmented in a large step size whenever possible; otherwise, the step size is reduced. Step size is controlled via removing the “small” edges out of residual graph.
Note: ∆ turns to be 1 finally; thus no edge in residual graph will be neglected.

3.1 Scaling Ford-Fulkerson algorithm具体应用

对于上面这个问题,我们用ScalingFord-Fulkerson算法求解:
具体程序如下:

/***********************************************************
Description:ScalingFordFulkerson算法求解网络最大流
Author:Robert.TY
Date:2016.11.30 
************************************************************/
#include<iostream>
#include<algorithm>
#include<queue>
#include<iomanip>
#include<limits.h>
#define Vnum   4//节点数目 
using namespace std;
class Max_Network_Flow{
public:
    int V ; 
    int graph[Vnum][Vnum];
    int max_flow;
    //构造函数 
    Max_Network_Flow(int tempV,int tempgraph[][Vnum]){
        V=tempV;
        for(int i=0;i<Vnum;i++){
            for(int j=0;j<Vnum;j++){
                graph[i][j]=tempgraph[i][j];
            }
        }
    }
    int Scaling_FordFulkersonMaxFlow(int s, int t,int tempdelta);//FordFulkerson算法求解最大流 
    bool PathSearch(int s, int t, int path[Vnum],int delta);//路径查询
    int Augment(int cur_flow,int path[Vnum],int s,int t);// 增加流操作 
    void ShowResult(int start,int end,int startdelta){
        max_flow=Scaling_FordFulkersonMaxFlow(start, end,startdelta);
        cout<<endl<<"max_flow="<<max_flow<<endl;
    }
};
int main(){
/*  int V = 6;
    int graph[Vnum][Vnum] = {   { 0, 0, 10, 10, 0, 0 }, 
                          { 0, 0, 0, 0, 0, 0 },
                          { 0, 0, 0, 2, 4, 8 },
                          { 0, 0, 0, 0, 0, 9 },
                          { 0, 10, 0, 0, 0, 0 },
                          { 0, 10, 0, 0, 6, 0 } }; */
    int V = 4;
    int graph[Vnum][Vnum] = {   { 0, 64, 32, 0 }, 
                                { 0, 0, 1, 64 },
                                { 0, 0, 0, 32 },
                                { 0, 0, 0, 0 }};

    cout<<"输出原始网络流图的邻接矩阵:"<<endl; 
    for(int i=0;i<Vnum;i++){
        for(int j=0;j<Vnum;j++){
            cout<<setw(5)<<graph[i][j]<<"  ";
        }
        cout<<endl;
    }
    int startdelta=96;
    int start=0;//出发节点 
    int end=3;//目的节点 
    Max_Network_Flow graph1(V,graph);
    graph1.ShowResult(start,end,startdelta) ;
    return 1;
}
int Max_Network_Flow::Scaling_FordFulkersonMaxFlow(int s, int t,int tempdelta){

    int maxFlow = 0;
    int path[Vnum];
    int delta=tempdelta;
    while(delta>=1){
        cout<<"delta="<<delta<<endl;
        while(PathSearch( s, t, path,delta)==1){//查询可行路径 
            cout<<"当delta="<<delta<<"时,找到路径"<<endl;    
            maxFlow=Augment(maxFlow,path,s,t);//再该路径进行增流操作 
        }   
        delta=delta/2;

    }
    cout<<"最后网络流的邻接矩阵:"<<endl;
    for(int i=0;i<Vnum;i++){
        for(int j=0;j<Vnum;j++){
            cout<<setw(5)<<graph[i][j]<<" ";
        }
        cout<<endl;
    }
    return maxFlow;
} 
bool Max_Network_Flow::PathSearch( int s, int t, int path[Vnum],int delta) {
    bool visited[Vnum] ;
    queue<int> queue1;
    queue1.push(s);
    visited[s] = true;
    //标准的BFS算法
    while(queue1.size() > 0){
        int top = queue1.front();//返回队列头部的元素, 
        queue1.pop();//出队操作 
        for(int i=0; i<V; i++){
            if(!visited[i] && graph[top][i] >= delta){//当节点i没有被访问,没有在队列当中且是top节点的邻居时
                queue1.push(i);
                visited[i] = true;
                path[i] = top;
            }

        }
    }
    return visited[t] == true;
}
int Max_Network_Flow:: Augment(int cur_flow,int path[Vnum],int s,int t){
    int min_flow =INT_MAX;
    //找到当前路径中最小的流量 bottleneck
    for(int v=t; v != s; v=path[v]){
        int u = path[v];
        min_flow=min_flow>graph[u][v]?graph[u][v]:min_flow;
    }
    //cout<<"min_flow="<<min_flow<<endl;
    //更新路径中的每条边反向边的权值增加min_flow,正向边的权值减少min_flow
    for(int v=t; v != s; v=path[v]){
        int u = path[v];
        graph[u][v] -= min_flow;
        graph[v][u] += min_flow;
    }
    cur_flow+=min_flow;
    return cur_flow;
}

程序运行结果如下;
这里写图片描述

4.总结

Ford-Fulkerson算法和ScalingFord-Fulkerson算法的使用,其边的容量必须都是整数。读者可以尝试选用一条边为小数的情况跑一跑这两个算法,比如0.618.当然网络流算法还有很多其他的算法。

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
Ford-Fulkerson算法是求解最大问题的一种经典算法。以下是一个基于增广路思想的Ford-Fulkerson算法的代码: ``` // 基于邻接矩阵的Ford-Fulkerson算法实现 #include <iostream> #include <queue> #include <cstring> using namespace std; const int MAXN = 100; // 最大顶点数 const int INF = 0x3f3f3f3f; // 表示无穷大 int n, m; // n表示顶点数,m表示边数 int s, t; // s表示源点,t表示汇点 int cap[MAXN][MAXN]; // 表示容量 int flow[MAXN][MAXN]; // 表示量 int pre[MAXN]; // 表示前驱节点 int bfs() { memset(pre, -1, sizeof(pre)); // 初始化前驱节点数组 queue<int> q; q.push(s); pre[s] = -2; while (!q.empty()) { int u = q.front(); q.pop(); for (int v = 0; v < n; ++v) { if (pre[v] == -1 && cap[u][v] > flow[u][v]) { pre[v] = u; if (v == t) return 1; q.push(v); } } } return 0; } int maxFlow() { int ans = 0; while (bfs()) { int minflow = INF; for (int u = t; u != s; u = pre[u]) { int v = pre[u]; minflow = min(minflow, cap[v][u] - flow[v][u]); } for (int u = t; u != s; u = pre[u]) { int v = pre[u]; flow[v][u] += minflow; flow[u][v] -= minflow; } ans += minflow; } return ans; } int main() { cin >> n >> m >> s >> t; memset(cap, 0, sizeof(cap)); memset(flow, 0, sizeof(flow)); for (int i = 0; i < m; ++i) { int u, v, c; cin >> u >> v >> c; cap[u][v] += c; // 注意有可能存在重边 } cout << maxFlow() << endl; return 0; } ``` 算法思路: 1. 初始化量为0; 2. 在剩余容量大于0的情况下,寻找增广路: - 从源点s开始,使用BFS寻找一条增广路; - 如果找到增广路,计算增广路上的最小剩余容量minflow,更新量; 3. 最大就是所有增广路上的最小剩余容量之和。 其中,增广路的定义是指从源点到汇点路径上,剩余容量均大于0的路径。在Ford-Fulkerson算法中,每次都需要寻找一条增广路来更新量,直到无法再找到增广路为止。这个过程中,每次找到的增广路都可以使得量增加,因此最终的量是不断增加的。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值