出處:http://www.cnblogs.com/bigmonkey
本文以學習、研究和分享爲主,如需轉載,請聯繫本人,標明做者和出處,非商業用途!
掃描二維碼關注公衆號「我是8位的」
在現實世界中,咱們的生活受到大量網絡的支配。網絡流能夠表示不少模型,好比管道中的石油、高壓線中電流,或者計算機網絡中的數據。網絡流也能夠解決不少問題,好比如何進行道路交通管控,以便有效地緩解早高峯的擁堵;在物流網運輸中,在知足供需關係的同時,怎樣使渠道成本最低;在轟炸機執行轟炸任務時,怎樣才能給敵軍補給線形成更嚴重的打擊。這些問題都有現成的網絡流算法,別再覺得網絡流僅僅是網絡中的比特流。算法
簡單地說,流網絡是一種有加權邊的有向圖。在數學中,網絡是這樣定義的:網絡(Networks)G=(V,E,s,t,C) 是一個五元組。其中(V, E)是一個有向圖,V是頂點的集合,E是邊的集合,它們都是非負實數集;s和t是(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,其它頂點表明中轉站;每條邊表明一個輸油管道,邊上的數字是管道的容量,數字越大,管道越粗,單位時間可以流過管道的油量也越大。把這些信息映射到網絡的定義,那麼:post
輸油管道會按期保養,不會產生漏油的狀況,更不會無緣無故生出石油,所以全部邊的容量都是正值,這種全部容量都是正數的st-網稱爲流網絡(Flow Network)。對於流網絡來講,頂點沒有容量。做爲一個樞紐,中轉站並不存儲石油,只負責經過開關控制石油流動的方向;一樣,石油只是流過輸油管道,並不會在管道中積累或彙集。學習
石油將在流網絡的管道中流動,這些流動的石油就是流網絡中的流。很顯然,管道中的石油不能超過管道的寬度。在流網絡模型上,流能夠看做另外一組隱藏的邊權值,稱爲邊流。流網絡有多條邊,固然也有多個流。對於輸油網的各個中轉站來講,因爲沒有存儲功能,所以流入的中轉站的石油等於流出中轉站的石油。spa
這些特色歸能夠納出流的定義:一個網絡G=(V,E,s,t,C)是流網絡,它的一個流是一個函數f,這個函數知足2個條件:計算機網絡
(1)容量限制,對於任意的邊 a, 0≤f(a)≤C(a),即邊的流不會大於該邊的容量。3d
(2)守恆定律,對於任意內部頂點v ,進入頂點的流量等於從該頂點發出的流量,簡稱「流入等於流出」。code
做爲流網絡的源頭,沒有流入原點的邊;相似地,也沒有從匯點流出的邊。假設石油從油田到煉油廠的傳輸過程當中沒有任何損失,那麼石油從源點的流出量等於匯點的流入量。
爲了方便地展現網絡中的流,咱們先對網絡的表示加以改進,用邊的寬度表示邊的容量,使寬度和容量成正比,管道越寬,容量也越大:
圖2
當石油流過輸油管道時,管道將被填充。咱們以實心箭頭表明石油,填充原來的管道,流的值和管道的容量成正比:
圖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中,網絡流的值是:
圖3所示的網絡流的值是2,在這個網絡中是否存在另一個值更大的網絡流?
既然網絡流的值取決於源點的流出量會或匯點的流入量,那麼只要使源點流出邊的容量或匯點流入邊的容量充分獲得利用,就能可以取得網絡流的最大值。這彷佛只是一個簡單的加法和比較運算:
只要盡全力填滿油田的輸油管道就行了:
圖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 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位的