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

网络和流网络

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

  ​

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

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

网络流(1)——网络、流网络和网络流_有向图_10