图算法-网络流的最大流问题

相关文章:
数据结构–图的概念
图搜索算法 - 深度优先搜索法(DFS)
图搜索算法 - 广度优先搜索法(BFS)
图搜索算法 - 拓扑排序
图搜索算法-最短路径算法-戴克斯特拉算法
图搜索算法-最短路径算法-贝尔曼-福特算法
图搜索算法-最小生成树问题-克鲁斯卡尔算法(kruskal)
图搜索算法-最小生成树问题-普里姆算法(prim)

网络流

首先认识网络流(network-flows),它是一种类比水流的解决问题方法,与线性规划密切相关。网络流的应用已遍及通讯、运输、电力、工程规划、任务分派、设备更新以及计算机辅助设计等众多领域。
哪一种图才叫网络流图?先看一个标准的网络流图是什么样子,如图所示。
在这里插入图片描述
(1)首先它是一个有向图。
(2)图中有唯一的一个源点(起点)S(入度为0的结点)。
(3)图中有唯一的一个汇聚点(终点)T(出度为0的结点)。
(4)图中每一条边的值都是非负数,其值为边可以通过的最大容量。
满足以上四个特征的图,便可以称为网络流图

注意:入度是指有向图的某个结点作为终点的次数和。出度是指有向图的某个结点作为起点的次数和。

最大流问题

最大流问题(maximum flow problem)是网络流图里面一种常见问题,它是一种组合最优化问题,就是要讨论如何充分利用装置的能力,使得运输的流量最大,以取得最好的效果。结合图6-28,最大流问题就是求得从起点【S】到终点【T】的最大流量。
面对这个问题,自然想到了贪心算法,每一条边都尽量使用最大流量,那么结果会不会是最大流量呢?请看下面例子,如图所示。
在这里插入图片描述
首先要知道两个基本规定。
(1)每一条的流量【f(e)】不超过其容量【c(e)】。
(2)除了起点S和终点T,流入量一定等于流出量。
现在可以通过深度优先或者广度优先寻找一条从起点到终点的路径,如【S-1-2-T】路径,每一条边的容量分别为【3,5,3】,所以此路径的最大流量为3,如图所示。
在这里插入图片描述
从上图得知没有其他通道了,那么最大流量就是3吗?还有其他通道没有用到,是不是浪费了?现在引入两个概念为残余容量(residual capacity)和残差图(Residual Graphs),定义如下。
R(e) = c(e) - f(e)
R(e)来表示残余容量,给流过的路径建立反向边,此网络流图也可以称为残留网络,如图所示。
在这里插入图片描述
在网络流图中还有一个规律,如图中从【1】到【2】已经用了3个单位流量(f=3),那么从正方向上残余容量为2(r=c-f=5-3=2)。从反方向上看,残余容量为3,可以这样理解,有多少流量就有多少残余容量。有了残留网络,便发现了一条新路径【S-2-1-T】,此路径上每一条边的残余容量为【2,3,2】,因此最大流量为2。此时再计算每条边上的流量f,【S-1】、【S-2】、【1-T】、【2-T】都比较简单,便是他们的最大流量。【1-2】边上的流量是多少了,可以知道【1-2】方向上的正向流量是3,反方向上的流量为2,因此【1-2】边上流量3-2=1,如图所示。
在这里插入图片描述
此网络流图的最大流量为5,一共两条路径,这两条路径可以称为增广路径。如路径【S-2-1-T】中,【S-1】和【1-T】是正向边,【2-1】是反向边。从刚才计算得知,正向边是增加流量,因此增加量不能让其流量大于其容量,反向边的流量则是减少流量,所以流量不能少于0,不然方向就改变了。
从贪心算法中增加残差图概念便是福德-富克逊(Ford-Fulkerson)算法,其算法步骤如下。

  • (1)初始化最大流量max_flow=0。
  • (2)从图中找一条增广路径P,若存在到步骤(3),若不存在返回max_flow,结束程序。
  • (3)在P上找路径所有边中最小流量值为路径流量path_flow。
  • (4)max_flow=max_flow+path_flow。
  • (5)在P上所有正向边残余容量减去path_flow,反向边增加path_flow。
  • (6)回到步骤(2)。

用此算法手动计算本节例子,如上图所示,以此来熟悉算法的计算过程。首先初始化最大流量max_flow为0,找到第一条增广路径【S-1-3-T】,每条边的残余容量为【4,2,4】,那么此路径最大流量为2,因此max_flow=2,此时网络流图状态如图所示。
在这里插入图片描述
同理找到第二条增广路径【S-1-2-T】,每条边的残余容量为【2,3,5】,此路径最大流量为2,因此max_flow=2+2=4,此时网络流图如图所示。
在这里插入图片描述
继续寻找增广路径,这条增广路径【S-3-T】,每条边上的残余容量为【7,2】,那么此路径最大流量为2,因此max_flow=4+2=6。下一条增广路径【S-3-1-2-T】,每条边上的残余容量为【5,2,1,3】,那么此路径最大流量为1,因此max_flow=6+1=7,此时网络流图的状态如下图所示。
在这里插入图片描述

注意:反向路径计算残余流量与正向路径的区别。如增广路径【S-3-1-2-T】中,边【3-1】是反向路径,因此残余容量为此时最大流量2,而不是容量与最大流量差0。

现在用代码表现此算法,首先需要用邻近矩阵表示网络流图,所以【GraphFordFulkerson】类将会复用【GraphArray】类进行图的输入,BFS()函数是广度优先搜索,略有变化,返回路径是规定了起点和终点。FordFulkerson()函数则为算法主程序,通过计算每条增广路径的最大流量求得总的最大流量,程序如下。

class GraphFordFulkerson(GraphArray): 
    # GraphArray
    def BFS(self, s, t, parent):
        """广度优先搜索"""
        visited =[False]*(self.amount) # 初始化所有结点都没有访问过
        queue=[] # 用队列结构来记录访问历史
        queue.append(s) # 首先访问起点s
        visited[s] = True
        while len(queue) > 0:   # 当队列为空,说明全部结点已经遍历完成 
            current_point = queue.pop(0)  # 获取访问历史的队头为当前结点
            for ind, val in enumerate(self.graph[current_point]):
                # 逐一访问当前结点的所有关联点
                if visited[ind] == False and val > 0:
                    queue.append(ind) # 如果没有访问添加到已访问队列中 
                    visited[ind] = True  # 标记结点为已访问
                    parent[ind] = current_point   # 记录遍历的路径链接
        return True if visited[t] else False  # 如果有路径能到终点t则返回True            
    def FordFulkerson(self, source, sink): 
        # 福德-富克逊算法,计算从起点到终点的最大流问题 
        parent = [-1]*(self.amount) # 这个列表记录路径链接
        max_flow = 0 # 初始化最大总流量为0 
        while self.BFS(source, sink, parent):  # 通过BFS来寻找增广路径
            path_flow = float("Inf") # 路径上的流量,初始化为无穷大
            s = sink 
            while(s != source):
                # 寻找此增广路径上的最小流量 
                path_flow = min(path_flow, self.graph[parent[s]][s]) 
                s = parent[s]
            max_flow += path_flow # 把此增广路径上的流量加到总流量上
            # 更新此增广路径上的残余容量
            v = sink 
            while(v != source): 
                u = parent[v]
                self.graph[u][v] -= path_flow  # 正向边减去路径流量
                self.graph[v][u] += path_flow  # 反向边加上路径流量
                v = parent[v]
        print("总的最大流量为:",max_flow)
        return max_flow   

以例子进行输入,验证程序结果。

g = GraphFordFulkerson(['s','1','2','3','t']) 
g.graph = [[0, 4, 0, 7, 0], 
        [0, 0, 3, 2, 0], 
        [0, 0, 0, 0, 5], 
        [0, 0, 0, 0, 4], 
        [0, 0, 0, 0, 0]] 
source = 0 # 起点下标
sink = 4 # 终点下标
g.FordFulkerson(source, sink)
#-------------------结果------------------------
总的最大流量为: 7

更多内容

想获取完整代码或更多相关图的算法内容,请查看我的书籍:《数据结构和算法基础Python语言实现》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值