福特-富爾克森算法

福特-富爾克森方法（Ford–Fulkerson method），又稱福特-富爾克森算法（Ford–Fulkerson algorithm），是一類計算網絡流的最大流的貪心算法。之所以稱之為“方法”而不是“算法”，是因為它尋找增廣路徑的方式並不是完全確定的，而是有幾種不同時間複雜度的實現方式。它在1956年由小萊斯特·倫道夫·福特及德爾伯特·雷·富爾克森發表。“福特-富爾克森”這個名詞通常也指代埃德蒙茲-卡普算法，這是一個特殊的福特-富爾克森算法實現。
算法的思想如下：只要有一條從源點（開始節點）到匯點（結束節點）的路徑，在路徑的所有邊上都有可用容量，就沿著這條路徑發送一個流，流量由路徑上的最小容量限制。 然後再找到另一條路徑，一直到網絡中不存在這種路徑為止。 一條有可用容量的路徑被稱為一條增廣路徑。

**算法**
設為一個圖，並且為每條從到的邊設置一個最大流量，並且初始化當前流量。下面是該算法每一步的實現：
這意味著每輪計算之後通過網絡的都是一個流。我們定義殘留網絡 是一個網絡的剩餘流量。注意殘留網絡可以設置從到的流量，即使在原先的網絡中不允許這種情況產生：如果  但 ，那麼：也即，從到的流量給從到的流量提供了額外的剩餘量。

**偽代碼**
算法 福特-富爾克森
輸入 給定一張邊的容量為的圖，源點以及匯點。
輸出 在網絡中，從到的最大流。
# 初始化網絡流量、殘留網絡。對於圖的每一條邊，初始化流量。
# 只要中還存在一條從到的路徑，使對於每一條邊，都有：
## 設置路徑本次應發送的流量為路徑最小剩餘流量：。
##更新網絡流量。
## 對於每一條邊，更新的剩餘流量：
###  （在路徑中“發送”流）
###  （這個流在之後可以被“發送”回來）
步驟2中的路徑可以用廣度優先搜索或深度優先搜索在中找到。如果使用了廣度優先搜索，這個算法就是Edmonds–Karp算法。
當步驟2中找不到可行路徑時，就無法在殘留網絡中到達。設是在殘留網絡中可以到達的節點的集合，然後從到的其餘部分的網絡一方面等於我們找到的從到的所有流的總流量，另一方面所有這樣的流量組成了一個上限。這說明我們找到的流是最大的。參見最大流最小割定理。
如果圖有多個源點和匯點，可以按如下方法處理：設，。 添加一個新源點與所有源點有一條邊相連，容量。添加一個新匯點與所有匯點 有一條邊相連，容量。然後執行福特-富爾克森算法。
同樣的，如果節點有通過限制，可將這個節點用兩個節點替換，用一條邊相連，容量為。然後執行福特-富爾克森算法。

**複雜度**
算法通過添加找到的增廣路徑的流量增加總流量，當無法找到增廣路徑時，總流量就達到了最大。當流量是整數時，福特-富爾克森算法的運行時間為（參見大O符號）, 圖中的邊數，為最大流。 這是因為一條增廣路徑可以在的時間複雜度內找到，每輪算法執行後流量的增長至少為。但是在極端情況下，算法有可能永遠不會停止。
福特-富爾克森算法的一個特例是埃德蒙茲-卡普算法，時間複雜度為。

**樣例**
下面的樣例演示了福特-富爾克森在一張有4個節點，源點為，匯點為的圖中的第一輪計算。 這個例子顯示了算法在最壞情況下的行為。在每一輪算法中，只有的流量被髮送至網絡中。如果算法改用寬度優先搜索，那麼只需要兩輪計算。
注意當找到路徑時，流是如何從發送至的。

**無法終止算法的樣例**
right
右圖所示的網絡中源點為，匯點為邊、、的容量為, 和，使。其它所有邊的容量。 使用福特-富爾克森算法可找到三條增廣路徑，分別為、、.
注意在步驟1和步驟5之後,邊、、的殘留容量都可以表示為、或，同時，對於一些特殊的這意味著算法可以通過、、與 無限增廣，並且殘留容量總處於一個循環。在步驟5之後網絡的流為，如果繼續用以上的算法增廣，總的流將向趨近，但最大流為。在這個樣例中，算法將永遠不會停止，且結果也不會向實際的最大流趨近。

**Python源碼 **
class Edge(object):
def __init__(self, u, v, w):
self.source = u
self.sink = v  
self.capacity = w
def __repr__(self):
return "%s->%s:%s" % (self.source, self.sink, self.capacity)
class FlowNetwork(object):
def __init__(self):
self.adj = {}
self.flow = {}

def add_vertex(self, vertex):
self.adjvertex = 

def get_edges(self, v):
return self.adjv

def add_edge(self, u, v, w=0):
if u == v:
raise ValueError("u == v")
edge = Edge(u,v,w)
redge = Edge(v,u,0)
edge.redge = redge
redge.redge = edge
self.adju.append(edge)
self.adjv.append(redge)
self.flowedge = 0
self.flowredge = 0

def find_path(self, source, sink, path):
if source == sink:
return path
for edge in self.get_edges(source):
residual = edge.capacity - self.flowedge
if residual > 0 and edge not in path:
result = self.find_path( edge.sink, sink, path + edge) 
if result != None:
return result

def max_flow(self, source, sink):
path = self.find_path(source, sink, )
while path != None:
residuals = edge.capacity - self.flowedge for edge in path
flow = min(residuals)
for edge in path:
self.flowedge += flow
self.flowedge.redge -= flow
path = self.find_path(source, sink, )
return sum(self.flowedge for edge in self.get_edges(source))

**使用樣例**
>>> g = FlowNetwork()
>>> g.add_vertex(v) for v in "sopqrt"
None, None, None, None, None, None
>>>
>>> g.add_edge('s','o',3)
>>> g.add_edge('s','p',3)
>>> g.add_edge('o','p',2)
>>> g.add_edge('o','q',3)
>>> g.add_edge('p','r',2)
>>> g.add_edge('r','t',3)
>>> g.add_edge('q','r',4)
>>> g.add_edge('q','t',2)
>>> print (g.max_flow('s','t'))
5

**應用**
二分圖的最大匹配
最大不相交路徑

**參考文獻**
 cite book |author1=George T. Heineman |author2=Gary Pollice |author3=Stanley Selkow | title= Algorithms in a Nutshell | publisher=Oreilly Media | year=2008 | chapter=Chapter 8:Network Flow Algorithms | pages = 226–250 | isbn=978-0-596-51624-6