流網絡(Flow Networks)指的是一個有向圖 G = (V, E),其中每條邊 (u, v) ∈ E 均有一非負容量 c(u, v) ≥ 0。如果 (u, v) ∉ E 則可以規定 c(u, v) = 0。流網絡中有兩個特殊的頂點:源點 s (source)和匯點 t(sink)。為方便起見,假定每個頂點均處於從源點到匯點的某條路徑上,就是說,對每個頂點 v ∈ E,存在一條路徑 s --> v --> t。因此,圖 G 為連通圖,且 |E| ≥ |V| - 1。
下圖展示了一個流網絡實例。
設 G = (V, E) 是一個流網絡,其容量函數為 c。設 s 為網絡的源點,t 為匯點。G 的流的一個實值函數 f:V×V → R,且滿足下列三個性質:
- 容量限制(Capacity Constraint):對所有頂點對 u, v ∈ V,要求 f(u, v) ≤ c(u, v)。
- 反對稱性(Skew Symmetry):對所有頂點對 u, v ∈ V,要求 f(u, v) = - f(v, u)。
- 流守恆性(Flow Conservation):對所有頂點對 u ∈ V - {s, t},要求 Σv∈Vf(u, v) = 0。
f(u, v) 稱為從頂點 u 到頂點 v 的流,流的值定義為:|f| =Σv∈Vf(s, v),即從源點 s 出發的總流。
最大流問題(Maximum-flow problem)中,給出源點 s 和匯點 t 的流網絡 G,希望找出從 s 到 t 的最大值流。
滿足流網絡的性質的實際上定義了問題的限制:
- 經過邊的流不能超過邊的容量;
- 除了源點 s 和匯點 t,對於其它所有頂點,流入量與流出量要相等;
上面的圖中描述的流網絡可簡化為下圖,其中源點 s = 0,匯點 t = 5。
上圖的最大流為 23,流向如下圖所示。
Ford-Fulkerson 算法是一種解決最大流的方法,其依賴於三種重要思想:
- 殘留網絡(Residual networks)
- 增廣路徑(Augmenting paths)
- 割(Cut)
這些思想是最大流最小割定理的精髓,該定理用流網絡的割來描述最大流的值。
最大流最小割定理
如果 f 是具有源點 s 和匯點 t 的流網絡 G = (V, E) 中的一個流,則下列條件是等價的:
- f 是 G 的一個最大流。
- 殘留網絡 Gf 不包含增廣路徑。
- 對 G 的某個割 (S, T),有 |f| = c(S, T)。
Ford-Fulkerson 算法是一種迭代方法。開始時,對所有 u, v ∈ V 有 f(u, v) = 0,即初始狀態時流的值為 0。在每次迭代中,可通過尋找一條增廣路徑來增加流值。增廣路徑可以看做是從源點 s 到匯點 t 之間的一條路徑,沿該路徑可以壓入更多的流,從而增加流的值。反復進行這一過程,直至增廣路徑都被找出為止。最大流最小割定理將說明在算法終止時,這一過程可產生出最大流。
1 FORD-FULKERSON-METHOD(G, s, t) 2 initialize flow f to 0 3 while there exists an augmenting path p 4 do augment flow f along p 5 return f
上述偽碼實現的時間復雜度為 O(max_flow * E)。
C# 代碼實現如下:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 5 namespace GraphAlgorithmTesting 6 { 7 class Program 8 { 9 static void Main(string[] args) 10 { 11 Graph g = new Graph(6); 12 g.AddEdge(0, 1, 16); 13 g.AddEdge(0, 2, 13); 14 g.AddEdge(1, 2, 10); 15 g.AddEdge(1, 3, 12); 16 g.AddEdge(2, 1, 4); 17 g.AddEdge(2, 4, 14); 18 g.AddEdge(3, 2, 9); 19 g.AddEdge(3, 5, 20); 20 g.AddEdge(4, 3, 7); 21 g.AddEdge(4, 5, 4); 22 23 Console.WriteLine(); 24 Console.WriteLine("Graph Vertex Count : {0}", g.VertexCount); 25 Console.WriteLine("Graph Edge Count : {0}", g.EdgeCount); 26 Console.WriteLine(); 27 28 int maxFlow = g.FordFulkerson(0, 5); 29 Console.WriteLine("The Max Flow is : {0}", maxFlow); 30 31 Console.ReadKey(); 32 } 33 34 class Edge 35 { 36 public Edge(int begin, int end, int weight) 37 { 38 this.Begin = begin; 39 this.End = end; 40 this.Weight = weight; 41 } 42 43 public int Begin { get; private set; } 44 public int End { get; private set; } 45 public int Weight { get; private set; } 46 47 public override string ToString() 48 { 49 return string.Format( 50 "Begin[{0}], End[{1}], Weight[{2}]", 51 Begin, End, Weight); 52 } 53 } 54 55 class Graph 56 { 57 private Dictionary<int, List<Edge>> _adjacentEdges 58 = new Dictionary<int, List<Edge>>(); 59 60 public Graph(int vertexCount) 61 { 62 this.VertexCount = vertexCount; 63 } 64 65 public int VertexCount { get; private set; } 66 67 public IEnumerable<int> Vertices 68 { 69 get 70 { 71 return _adjacentEdges.Keys; 72 } 73 } 74 75 public IEnumerable<Edge> Edges 76 { 77 get 78 { 79 return _adjacentEdges.Values.SelectMany(e => e); 80 } 81 } 82 83 public int EdgeCount 84 { 85 get 86 { 87 return this.Edges.Count(); 88 } 89 } 90 91 public void AddEdge(int begin, int end, int weight) 92 { 93 if (!_adjacentEdges.ContainsKey(begin)) 94 { 95 var edges = new List<Edge>(); 96 _adjacentEdges.Add(begin, edges); 97 } 98 99 _adjacentEdges[begin].Add(new Edge(begin, end, weight)); 100 } 101 102 public int FordFulkerson(int s, int t) 103 { 104 int u, v; 105 106 // Create a residual graph and fill the residual graph with 107 // given capacities in the original graph as residual capacities 108 // in residual graph 109 int[,] residual = new int[VertexCount, VertexCount]; 110 111 // Residual graph where rGraph[i,j] indicates 112 // residual capacity of edge from i to j (if there 113 // is an edge. If rGraph[i,j] is 0, then there is not) 114 for (u = 0; u < VertexCount; u++) 115 for (v = 0; v < VertexCount; v++) 116 residual[u, v] = 0; 117 foreach (var edge in this.Edges) 118 { 119 residual[edge.Begin, edge.End] = edge.Weight; 120 } 121 122 // This array is filled by BFS and to store path 123 int[] parent = new int[VertexCount]; 124 125 // There is no flow initially 126 int maxFlow = 0; 127 128 // Augment the flow while there is path from source to sink 129 while (BFS(residual, s, t, parent)) 130 { 131 // Find minimum residual capacity of the edhes along the 132 // path filled by BFS. Or we can say find the maximum flow 133 // through the path found. 134 int pathFlow = int.MaxValue; 135 for (v = t; v != s; v = parent[v]) 136 { 137 u = parent[v]; 138 pathFlow = pathFlow < residual[u, v] 139 ? pathFlow : residual[u, v]; 140 } 141 142 // update residual capacities of the edges and reverse edges 143 // along the path 144 for (v = t; v != s; v = parent[v]) 145 { 146 u = parent[v]; 147 residual[u, v] -= pathFlow; 148 residual[v, u] += pathFlow; 149 } 150 151 // Add path flow to overall flow 152 maxFlow += pathFlow; 153 } 154 155 // Return the overall flow 156 return maxFlow; 157 } 158 159 // Returns true if there is a path from source 's' to sink 't' in 160 // residual graph. Also fills parent[] to store the path. 161 private bool BFS(int[,] residual, int s, int t, int[] parent) 162 { 163 bool[] visited = new bool[VertexCount]; 164 for (int i = 0; i < visited.Length; i++) 165 { 166 visited[i] = false; 167 } 168 169 Queue<int> q = new Queue<int>(); 170 171 visited[s] = true; 172 q.Enqueue(s); 173 parent[s] = -1; 174 175 // standard BFS loop 176 while (q.Count > 0) 177 { 178 int u = q.Dequeue(); 179 180 for (int v = 0; v < VertexCount; v++) 181 { 182 if (!visited[v] 183 && residual[u, v] > 0) 184 { 185 q.Enqueue(v); 186 visited[v] = true; 187 parent[v] = u; 188 } 189 } 190 } 191 192 // If we reached sink in BFS starting from source, 193 // then return true, else false 194 return visited[t] == true; 195 } 196 } 197 } 198 }
運行結果如下:
參考資料
- 廣度優先搜索
- 深度優先搜索
- Breadth First Traversal for a Graph
- Depth First Traversal for a Graph
- Dijkstra 單源最短路徑算法
- Bellman-Ford 單源最短路徑算法
- Bellman–Ford algorithm
- Introduction To Algorithm
- Floyd-Warshall's algorithm
- Bellman-Ford algorithm for single-source shortest paths
- Dynamic Programming | Set 23 (Bellman–Ford Algorithm)
- Dynamic Programming | Set 16 (Floyd Warshall Algorithm)
- Johnson’s algorithm for All-pairs shortest paths
- Floyd-Warshall's algorithm
- 最短路徑算法--Dijkstra算法,Bellmanford算法,Floyd算法,Johnson算法
- QuickGraph, Graph Data Structures And Algorithms for .NET
- CHAPTER 26: ALL-PAIRS SHORTEST PATHS
本篇文章《Ford-Fulkerson 最大流算法》由 Dennis Gao 發表自博客園,未經作者本人同意禁止任何形式的轉載,任何自動或人為的爬蟲轉載行為均為耍流氓。