2020-5-28-算法导论-图的最大流

​网络最大流

1: 基本概率

2: 算法原理

3: Ford-Fulkerson,Edmonds-Karp算法C++代码实现(基于图的邻接矩阵实现)

什么是网络最大流问题?

网络最大流问题是关于流网络的:在不违背容量限制的条件下,求出从源点到汇点的最大流量。

什么是流网络?

  1. G=(V,E)是一个有向图

  2. 其中每条边(u,v)有一个非负的容量值c(u,v)

  3. 其中E包含任意一条边(u,v),那么图中就不存在它的反向边。

  4. 有两个特殊的结点,源结点s和汇点t。

     

流网络满足的性质。

G中的流是一个实值函数f,满足以下3条性质

1. 容量限制性(capacity contraint):对于所有的结点u,v,

2. 流量守恒性(flow conservation):对于所有的非源点和汇点的结点u,v

3:反对称性(skew symmetry)对于所有的结点u,v

f[u,v] = - f[v,u]

  给定源点s和汇点t的流网络G,找一个从s到t流量最大的流?

1:Ford-Fulkerson方法

2:Edmonds-Karp算法

Ford-Fulkerson方法是一种迭代方法,它有不同运行时间的几种实现版本,它的正确性基于最大流最小割定理。该定理用流网络的切割来描述最大流的值。

有三个重要概念: 残存网络、增广路径和切割

1: 残存网络(residual network)

假定有一个流网络G=(V,E),其源点为s,汇点为t,f为G中的一个流。对即诶点对u,v,定义残存容量(residual capacity),有:

 

 

2. 增广路径(augmenting paths)

给定流网络G和流f,增广路径p是残存网络中一条从源结点s到汇点t的简单路径

引理 : 设流网络G=(V,E),f是G的一个流,并设p是Gf中的一条增广路径。定义函数fp:

令f^′=f+fp,则f^′是G上的一个流,其值为|f^′|=|f|+|fp|>|f|。

3流网络的切割(cuts of networks)

流网络G=(V,E)的切割(S,T)是将V划分为S和T=V-S两部分,使得s∈S,t∈T。若f是一个流,则定义横跨切割(S,T)的净流量f(S,T)

 

 

切割(S,T)的容量记为c(S,T):

 

流网络的最小切割就是网络中所有切割中容量最小的切割。

算法过程:

  1. 开始时,流的值为0,对所有u,v∈V,有f(u,v)=0。

  2. 每次迭代通过“增广路径”来追加流。增广路径是残留网络中从源点s到汇点t之间的一条有向路径。沿该路径可以增加流的值。

  3. 经过反复迭代,直到增广路径被全部找出为止。最大流最小割定理说明在算法终止时,Ford-Fulkerson方法计算出最大流。

Edmonds-Karp算法

如果在Ford-Fulkerson算法中用广度优先搜索来实现对增广路径p的计算,即如果增广路径是残存网络中从s到t的最短路径(其中每条边为单位距离,或权),则能够改进Ford-Fulkerson的时间上界。即Edmonds-Karp算法的运行时间为  O(|v| |e|^2)      

 算法过程:

     (1)  BFS寻找增广路,若能找到,转步骤(2).否则转步骤(3) 

  (2) 更新剩余容量,将流累加. 

  (3) 输出累加的流,即为最大流

求下图最大流:maxflow 7

 

 

 

算法实现:

#include <iostream>#include<cstring>using namespace std ;#define MaxSize 50      //图最大顶点个数#define inf 999999999  //无穷大值,在图中表示不连通typedef  int  datatype; //可以根据需要改变数据类型,也可以自己封装数据类型//邻接矩阵typedef struct{    int num;    //顶点编号    char info;  //顶点信息}Node ; // 顶点类型typedef struct{  datatype Capacity[MaxSize][MaxSize] ; //保存图的,边集权重信息  int n,e ;   // n 顶点的总数, e 边数  Node nodes[MaxSize] ; //保存顶点信息}MGraph;int visited[MaxSize] ;   //访问记录数组int pre[MaxSize] ;   //保存当前访问结点的前一个已经访问节点/*  构造图的初始函数 */MGraph InitiaL_Graph(){    MGraph graph ;    int n ,e ;  // n: 结点数 e: 边数    int i, j ;    cout<<"初始化图操作:请输入你要构造的图的 顶点数(n) 和边数(e)"<<endl ;    cout<<"请输入结点数n: \t";    cin>>n;    cout<<"请输入变数e: \t";    cin>>e;    graph.n = n ;    graph.e = e ;    for(i=1;i<=n;i++ ){        for(j=1;j<=n;j++){           graph.Capacity[i][j] =-inf; 把每条边赋值-inf,表示不连通        }    }    cout<<"请输入边值权重"<<endl ;    cout<<" edgs[i][j] = weight,如果没有图中没有weight,请全输入1占位: 输入格式 i ,j ,weight"<<endl ;    while(e--){        datatype temp ;        cin>>i>>j>>temp ;        graph.Capacity[i][j] = temp ;        graph.Capacity[j][i] = 0 ; //反向表初始为0    }    return graph ;}/*  BFS() find 图中 s->t 最短增光路径return true :  找增光路径成功return false:寻找增光路失败 */bool BFS(MGraph graph ,int S , int T){    int  Queue[MaxSize] ,Front=0,Rear = 0 ; //借助队列实现BFS    memset(visited,0,sizeof(visited)) ;  // 初始化    memset(pre,-1,sizeof(pre)) ;    for(int i=1;i<=graph.n;i++){ //为安全再赋值初始化一次        visited[i] = 0 ;        pre[i] =-1;    }    int u;    visited[S] = 1 ;    pre[S] =S ;    Rear =(Rear+1) %MaxSize ;    Queue[Rear] = S ;    while(Front != Rear){        Front = (Front +1) %MaxSize ; //出队操作        u = Queue[Front] ;        for(int v = 1 ;v<=graph.n;v++){            if(visited[v]==0 && graph.Capacity[u][v]>0 ){ //BFS遍历                pre[v] =u ;                visited[v]=1 ;                if( v == T)   //当达到终点 ,返回true 此次寻找增光路径结束                    return true ;                Rear =(Rear+1)%MaxSize ; //入队操作                Queue[Rear] = v ;            }        }    }    return false ;  //BFS 结束,也没找打终点,则返回false ,表示已经没有S-》T 增光路径}/* EK算法:利用BFS寻找最短增光路径 计算网络最大流 S : 路径起点 T :路径终点 return:maxFlow 网络最大流*/datatype  EdmondsKrap(MGraph graph ,int S , int T){    datatype maxFlow = 0 ; //网络最大流    while(BFS(graph,S,T)){  //BFS 不停寻找增光路径        datatype dlta = inf ;   //每条路径最大流量,等于最小割        for(int i = T ;i!=S; i = pre[i]){  //回溯这条增光路径,找到这条路径的最大流             dlta =min(dlta,graph.Capacity[pre[i]][i]) ; // 这条路径,就是管子最细的地方为最大流量        }        for(int i =T;i!=S; i= pre[i]){            graph.Capacity[pre[i]][i] -= dlta ;  //更新网络            graph.Capacity[i][pre[i]] += dlta ;  //反向边        }        maxFlow +=dlta ; //累加    }   return maxFlow ;}/*DFS 深度优先遍历,寻找图中s->t 增广路径当 pre[T] = -1 ,则图中就找不到s->t增广路径*/void DFS(MGraph graph, int S ) // DFS 寻找增光路径{    visited[S] =1 ;    for(int i ; i<=graph.n ;i++){        if(!visited[i] && graph.Capacity[S][i]>0){            visited[i] = 1;            pre[i] = S ;   //这条路径,保存访问这个结点的上一个访问结点            DFS(graph,i);        }    }}/*Ford_Fulkerson: DFS寻找增广路径求网络最大流 S : 路径起点 T :路径终点 return:maxFlow 网络最大流*/int Ford_Fulkerson(MGraph graph , int S , int T){    datatype maxFlow = 0 ;  //网络最大流    memset(visited,0,sizeof(visited)) ; //初始化操作    memset(pre,-1,sizeof(pre)) ;    for(int i=1;i<=graph.n;i++){        visited[i] = 0 ;   //保险,在设置为0 ,表示没有遍历        pre[i] =-1;   // 设置-1    }    while(true){  //不停的寻找曾广路,直到S,T没有路径连通结束        DFS(graph,S) ;        if(pre[T]==-1)   // T: 终结点 pre[T]=-1 ,表示已经找不到增广路径,不通了。          break ;        datatype dlta = inf ;  //每条路径最大流,等于最小割        for(int i = T ;i!=S; i = pre[i]){  //回溯这条增光路径,找到这条路径的最大流            dlta =min(dlta,graph.Capacity[pre[i]][i]) ;        }        for(int i =T;i!=S; i= pre[i]){            graph.Capacity[pre[i]][i] -= dlta ;  //剩余网络            graph.Capacity[i][pre[i]] += dlta ;  //反向边        }        maxFlow +=dlta ; //累加       for(int i=1;i<=graph.n;i++){ //继续下一条增光路的求解            visited[i] = 0 ;            pre[i] =-1;        }    }    return maxFlow ;}int main(){     int i,s,t ;    MGraph graph = InitiaL_Graph() ;    s=1 ;    t=6 ;    cout<<"EdmondsKrap求解最大流:" ;    cout<<EdmondsKrap(graph,s,t) <<endl ;    cout<<"Ford_Fulkerson 求解最大流" ;    cout<<Ford_Fulkerson(graph,s,t)<<endl ;    return (1) ;}/*输入  6  81 2 31 3 42 4 12 5 53 5 53 6 44 6 25 6 6*/

 

下图的最大流 maxFlow  =2 

输入:n = 4 , e = 5    

1 2 1

1 3 1

2 3 1

2 4 1

3 4 1

注意程序中 s: 设置1     t : 设置 4

Reference:

[1]:https://blog.csdn.net/weixin_30104533/article/details/80659364

[2]:https://images0.cnblogs.com/blog/464052/201403/291452151415012.jpg

[3]: 算法导论第三版   

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值