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

网络和流网络

  简单地说,流网络是一种有加权边的有向图。在数学中,网络是这样定义的:网络(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

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

网络流(1)——网络、流网络和网络流_算法_02

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

流、网络流和网络流的值

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

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

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

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

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

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

网络流(1)——网络、流网络和网络流_网络流_03

图2

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

网络流(1)——网络、流网络和网络流_数据结构_04

图3

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

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

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

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

网络流(1)——网络、流网络和网络流_数据结构_05

最大流

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

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

网络流(1)——网络、流网络和网络流_数据结构_06

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

网络流(1)——网络、流网络和网络流_算法_07

图4

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

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

网络流(1)——网络、流网络和网络流_网络流_08

基本数据结构

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

 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()

网络流(1)——网络、流网络和网络流_网络流_09

 


 

    作者:我是8位的

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

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

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

网络流(1)——网络、流网络和网络流_网络流_10