zoukankan      html  css  js  c++  java
  • 网络流(1)——网络、流网络和网络流

      在现实世界中,我们的生活受到大量网络的支配。网络流可以表示很多模型,比如管道中的石油、高压线中电流,或者计算机网络中的数据。网络流也可以解决很多问题,比如如何进行道路交通管控,以便有效地缓解早高峰的拥堵;在物流网运输中,在满足供需关系的同时,怎样使渠道成本最低;在轰炸机执行轰炸任务时,怎样才能给敌军补给线造成更严重的打击。这些问题都有现成的网络流算法,别再以为网络流仅仅是网络中的比特流。

    网络和流网络

      简单地说,流网络是一种有加权边的有向图。在数学中,网络是这样定义的:网络(Networks)G=(V,E,s,t,C) 是一个五元组。其中(V, E)是一个有向图,V是顶点的集合,E是边的集合,它们都是非负实数集;st是(V, E)中的两个不同顶点;s的入度为0(没有指向s的边),是G的源点(source);t的出度为0(没有边从t发出),是G的汇点(sink);C是容量函数,对于(V, E)中的任意有向边 a,称 C(a)是边 a 的容量(capacity)。对于仅有一个指定的原点s和指定的汇点t的网络,称为st-网。

      实际上网络的概念相当直白,我们以一个简单的物理模型直观地解释网络。假设有一组联通的输油管道,管道连接处的中转站设有控制开关。这组管道的源头是一个油田,汇点是一个炼油厂,石油从油田流出,最终汇入炼油厂:

    图1

      图1是一个带有加权边的有向图,也是一个典型的st-网。其中v1代表油田,是源点s,v6代表炼油厂,是汇点t,其它顶点代表中转站;每条边代表一个输油管道,边上的数字是管道的容量,数字越大,管道越粗,单位时间能够流过管道的油量也越大。把这些信息映射到网络的定义,那么:

      输油管道会定期保养,不会产生漏油的情况,更不会平白无故生出石油,因此所有边的容量都是正值,这种所有容量都是正数的st-网称为流网络(Flow Network)。对于流网络来说,顶点没有容量。作为一个枢纽,中转站并不存储石油,只负责通过开关控制石油流动的方向;同样,石油只是流过输油管道,并不会在管道中积累或聚集。

    流、网络流和网络流的值

      石油将在流网络的管道中流动,这些流动的石油就是流网络中的流。很显然,管道中的石油不能超过管道的宽度。在流网络模型上,流可以看作另一组隐藏的边权值,称为边流。流网络有多条边,当然也有多个流。对于输油网的各个中转站来说,由于没有存储功能,因此流入的中转站的石油等于流出中转站的石油。

      这些特点归可以纳出流的定义:一个网络G=(V,E,s,t,C)是流网络,它的一个流是一个函数f,这个函数满足2个条件:

      (1)容量限制,对于任意的边 a, 0≤f(a)≤C(a),即边的流不会大于该边的容量。

      (2)守恒定律,对于任意内部顶点v ,进入顶点的流量等于从该顶点发出的流量,简称“流入等于流出”。

      作为流网络的源头,没有流入原点的边;类似地,也没有从汇点流出的边。假设石油从油田到炼油厂的传输过程中没有任何损失,那么石油从源点的流出量等于汇点的流入量。

      为了方便地展示网络中的流,我们先对网络的表示加以改进,用边的宽度表示边的容量,使宽度和容量成正比,管道越宽,容量也越大:

    图2

      当石油流过输油管道时,管道将被填充。我们以实心箭头代表石油,填充原来的管道,流的值和管道的容量成正比:

    图3

      在图3中,边v2v4的容量是1,流也是1,此时我们说这条边是满边,用星号表示满边的流值。边v1v3的容量是3,流是1,说明这条管道并没有得到充分利用,实心箭头填充了管道1/3的空间。边v5v4的容量是1,流是0,v5v4处的开关是闭合的,这条管道处于闲置状态。

      图3也展示了从源点到汇点的所有流,因此我们也称图3是一个网络流(network-flows)。

      流网络、流、网络流,看起来极为相似,很多资料中也互相混用,但三者还是有所区别。网络流是指所有容量都是正数的st-网;流代表个体,特指某一条边上的流量;网络流代表整体,表示流网络上所有流的集合。此外,网络流还有另一个含义,指用流网络的模型找出解决问题的方法。网络流的含义究竟是集合还是方法,需要根据具体的上下文而定。通常来讲,这些概念在实际问题中非常直白,不必太过纠结。

      源点的流出量或汇点的流入量称为网络流的值。在图8.3中,网络流的值是:

    最大流

      图3所示的网络流的值是2,在这个网络中是否存在另外一个值更大的网络流?

      既然网络流的值取决于源点的流出量会或汇点的流入量,那么只要使源点流出边的容量或汇点流入边的容量充分得到利用,就能能够取得网络流的最大值。这似乎只是一个简单的加法和比较运算:

      只要尽全力填满油田的输油管道就好了:

    图4

      遗憾的是,这种方式是错误的,它忽略了其它边的容量,破坏了“流入等于流出”的守恒定律。以图4为例,C(v2v4)和C(v3v5)的总容量是3,不足以容纳5个单位的石油。换句话说,下游的容量制约了上游的生产力。

      油田通过两条管道输出石油,其中的一条路径是v1v2v4v6,当f(v1v2)=1时,v2v4是满边,它已经被充分利用,根据守恒定律,这条路径上的总流量是1。类似地,另一条路径v1v3v5v6的总流量也是1。别忘了,我们在v5v4处还有一个开关,打开这个开关,会得到另一条路径v1v3v5v4v6,这将使更多的管道得到充分利用,此时得到了网络中的最大流,Fmax=3:

    基本数据结构

      以下是网络流的基本数据结构,后续章节将反复用到并扩充这个结构:

     1 class Edge():
     2     ''''''
     3     def __init__(self, v_from:int, v_to:int, cap:int, flow=0):
     4         '''
     5         :param v_from: 起点
     6         :param v_to:   终点
     7         :param cap:    容量
     8         :param flow:   流
     9         '''
    10         self.v_from, self.v_to = v_from, v_to
    11         self.cap, self.flow = cap, flow
    12
    13     def is_from(self, v):
    14         ''' 是否是v顶点的流入边 '''
    15         return self.v_from == v
    16
    17     def is_to(self, v):
    18         ''' 是否是v顶点的流出边 '''
    19         return self.v_to == v
    20
    21     def __str__(self):
    22         return str(self.v_from) + '' + str(self.v_to)
    23
    24 class Network():
    25     ''' st-网络 '''
    26     def __init__(self, V:list, E:list, s:int, t:int):
    27         '''
    28         :param V: 顶点集
    29         :param E: 边集
    30         :param s: 原点
    31         :param e: 汇点
    32         :return:
    33         '''
    34         self.V, self.E, self.s, self.t = V, E, s, t
    35
    36     def get_from_edges(self, v):
    37         '''
    38         获取顶点的流入边
    39         :param v: 顶点值
    40         :return: 顶点的流入边list
    41         '''
    42         return [edge for edge in self.E if edge.is_from(v)]
    43
    44     def get_to_edges(self, v):
    45         '''
    46         获取顶点的流出边
    47         :param v: 顶点值
    48         :return: 顶点的流出边list
    49         '''
    50         return [edge for edge in self.E if edge.is_to(v)]
    51
    52     def flows_from(self, v):
    53         '''v顶点的流入量 '''
    54         edges = self.get_from_edges(v) # v的流入边
    55         return sum([e.flow for e in edges])
    56
    57     def flows_to(self, v):
    58         '''v顶点的流出量 '''
    59         edges = self.get_to_edges(v)  # v的流出边
    60         return sum([e.flow for e in edges])
    61
    62     def check(self, s, t):
    63         ''' 源点的流出是否等于汇点的流入
    64         :param s: 源点
    65         :param t: 汇点
    66         :return: s流出 = t流入,返回true
    67         '''
    68         return self.flows_to(s) == self.flows_from(t)
    69
    70     def display(self):
    71         print('%-10s%-8s%-8s' % ('', '容量', ''))
    72         for e in self.E:
    73             print('%-10s%-10s%-8s' % (e, e.cap, e.flow))
    74
    75 V = [1, 2, 3, 4, 5, 6]
    76 E = [Edge(1, 2, 2), Edge(1, 3, 3), Edge(2, 4, 1), Edge(3, 5, 2),
    77      Edge(4, 6, 2), Edge(5, 4, 1), Edge(5, 6, 1)]
    78 s, t = 1, 6
    79 G = Network(V, E, s, t)
    80 G.display()


        作者:我是8位的

      出处:http://www.cnblogs.com/bigmonkey

      本文以学习、研究和分享为主,如需转载,请联系本人,标明作者和出处,非商业用途! 

      扫描二维码关注公众号“我是8位的”

  • 相关阅读:
    MongoDB的基本操作
    Python 进阶 之 协程
    sublime text3 3143 注册码
    git add 文档
    Corosync 配置描述
    Centos 7 设置 DNS
    2017百度春招<度度熊买帽子的问题>
    leetcode 160. Intersection of Two Linked Lists
    leetcode 155. Min Stack
    leetcode 141 142. Linked List Cycle
  • 原文地址:https://www.cnblogs.com/bigmonkey/p/10878112.html
Copyright © 2011-2022 走看看