**福特-富尔克森方法**（英語：），又稱**福特-富尔克森算法**（），是一类计算网络流的最大流的贪心算法。之所以称之为"方法"而不是"算法"，是因为它寻找增广路径的方式并不是完全确定的，而是有几种不同时间复杂度的实现方式。它在1956年由小萊斯特·倫道夫·福特及德爾伯特·雷·富爾克森发表。"福特-富尔克森"这个名词通常也指代埃德蒙兹-卡普算法，这是一个特殊的福特-富尔克森算法实现。

算法的思想如下：只要有一条从源点（开始节点）到汇点（结束节点）的路径，在路径的所有边上都有可用容量，就沿着这条路径发送一个流，流量由路径上的最小容量限制。 然后再找到另一条路径，一直到网络中不存在这种路径为止。 一条有可用容量的路径被称为一条增广路径。

算法
--

设{\displaystyle G(V,E)}为一个图，并且为每条从u到v的边(u,v)设置一个最大流量c(u,v)，并且初始化当前流量{\displaystyle f(u,v)=0}。下面是该算法每一步的实现：

|  |  |  |
| --- | --- | --- |
| **容量限制**: | {\displaystyle \forall (u,v)\in E,f(u,v)\leq c(u,v)} | 每条边上的流都不能超出边的最大流量。 |
| **反向对称**: | {\displaystyle \forall (u,v)\in E,f(u,v)=-f(v,u)} | 从u到v的流量一定是从v到u的流量的相反数（见样例）。 |
| **流量守恒**: | {\displaystyle \forall u\in V:u\neq s,u\neq t\Rightarrow \sum \_{w\in V}f(u,w)=0} | 除非u是源点s或汇点t，一个节点的净流量为零。 |
| **f的值**: | {\displaystyle \sum \_{(s,u)\in E}f(s,u)=\sum \_{(v,t)\in E}f(v,t)} | 从源点s流出的流量一定等于汇点t接收的流量。 |
|

这意味着每轮计算之后通过网络的都是一个流。我们定义**残留网络** {\displaystyle G\_{f}(V,E\_{f})}是一个网络的剩余流量{\displaystyle c\_{f}(u,v)=c(u,v)-f(u,v)}。注意残留网络可以设置从v到u的流量，即使在原先的网络中不允许这种情况产生：如果 {\displaystyle c(v,u)=0} 但 {\displaystyle f(u,v)>0}，那么{\displaystyle c\_{f}(v,u)=c(v,u)-f(v,u)=f(u,v)>0}：也即，从u到v的流量给从v到u的流量提供了额外的剩余量。

### 伪代码

**算法** 福特-富尔克森

**输入** 给定一张边的容量为c的图G = (V,E)，源点s以及汇点t。
**输出** 在网络G中，从s到t的最大流f。
1. 初始化网络流量{\displaystyle f\leftarrow 0}、残留网络{\displaystyle G\_{f}\leftarrow G}。对于图的每一条边(u,v)，初始化流量{\displaystyle f(u,v)\leftarrow 0}。
2. 只要{\displaystyle G\_{f}}中还存在一条从s到t的路径p，使对于每一条边{\displaystyle (u,v)\in p}，都有{\displaystyle c\_{f}(u,v)>0}：
	1. 设置路径p本次应发送的流量为路径最小剩余流量：{\displaystyle c\_{f}(p)\leftarrow \min \_{(u,v)\in p}c\_{f}(u,v)}。
	2. 更新网络流量{\displaystyle f\leftarrow f+c\_{f}(p)}。
	3. 对于每一条边{\displaystyle (u,v)\in p}，更新{\displaystyle G\_{f}}的剩余流量：
		1. {\displaystyle f(u,v)\leftarrow f(u,v)+c\_{f}(p)} （*在路径中"发送"流）*
		2. {\displaystyle f(v,u)\leftarrow f(v,u)-c\_{f}(p)} （*这个流在之后可以被"发送"回来）*

步骤2中的路径p可以用广度优先搜索或深度优先搜索在{\displaystyle G\_{f}(V,E\_{f})}中找到。如果使用了广度优先搜索，这个算法就是Edmonds–Karp算法。

当步骤2中找不到可行路径时，s就无法在残留网络中到达t。设S是在残留网络中s可以到达的节点的集合，然后从S到V的其余部分的网络一方面等于我们找到的从s到t的所有流的总流量，另一方面所有这样的流量组成了一个上限。这说明我们找到的流是最大的。参见最大流最小割定理。

如果图{\displaystyle G(V,E)}有多个源点和汇点，可以按如下方法处理：设{\displaystyle T=\{t|t{\text{为 目 标 点 }}\}}，{\displaystyle S=\{s|s{\text{为 源 点 }}\}}。 添加一个新源点{\displaystyle s^{\*}}与所有源点有一条边{\displaystyle (s^{\*},s)}相连，容量{\displaystyle c(s^{\*},s)=d\_{s}\;(d\_{s}=\sum \_{(s,u)\in E}c(s,u))}。添加一个新汇点{\displaystyle t^{\*}}与所有汇点{\displaystyle (t,t^{\*})} 有一条边相连，容量{\displaystyle c(t,t^{\*})=d\_{t}\;(d\_{t}=\sum \_{(v,t)\in E}c(v,t))}。然后执行福特-富尔克森算法。

同样的，如果节点u有通过限制{\displaystyle d\_{u}}，可将这个节点用两个节点{\displaystyle u\_{in},u\_{out}}替换，用一条边{\displaystyle (u\_{in},u\_{out})}相连，容量为{\displaystyle c(u\_{in},u\_{out})=d\_{u}}。然后执行福特-富尔克森算法。

复杂度
---

算法通过添加找到的增广路径的流量增加总流量，当无法找到增广路径时，总流量就达到了最大。当流量是整数时，福特-富尔克森算法的运行时间为{\displaystyle O(Ef)}（参见大O符号）, E图中的边数，f为最大流。 这是因为一条增广路径可以在{\displaystyle O(E)}的时间复杂度内找到，每轮算法执行后流量的增长至少为1。但是在极端情况下，算法有可能永远不会停止。

福特-富尔克森算法的一个特例是埃德蒙兹-卡普算法，时间复杂度为{\displaystyle O(VE^{2})}。

样例
--

下面的样例演示了福特-富尔克森在一张有4个节点，源点为A，汇点为D的图中的第一轮计算。 这个例子显示了算法在最坏情况下的行为。在每一轮算法中，只有1的流量被发送至网络中。如果算法改用宽度优先搜索，那么只需要两轮计算。

| 路径 | 容量 | 网络 |
| --- | --- | --- |
| 原流 |  |
| {\displaystyle A,B,C,D} | {\displaystyle {\begin{aligned}&\min(c\_{f}(A,B),c\_{f}(B,C),c\_{f}(C,D))\\=&\min(c(A,B)-f(A,B),c(B,C)-f(B,C),c(C,D)-f(C,D))\\=&\min(1000-0,1-0,1000-0)=1\end{aligned}}} |  |
| {\displaystyle A,C,B,D} | {\displaystyle {\begin{aligned}&\min(c\_{f}(A,C),c\_{f}(C,B),c\_{f}(B,D))\\=&\min(c(A,C)-f(A,C),c(C,B)-f(C,B),c(B,D)-f(B,D))\\=&\min(1000-0,0-(-1),1000-0)=1\end{aligned}}} |  |
| 1998轮之后… |
| 最终流 |  |

注意当找到路径{\displaystyle A,C,B,D}时，流是如何从C发送至B的。

无法终止算法的样例
---------

右图所示的网络中源点为s，汇点为t边e\_{1}、e\_{2}、e\_{3}的容量为1, {\displaystyle r=({\sqrt {5}}-1)/2}和1，使{\displaystyle r^{2}=1-r}。其它所有边的容量{\displaystyle M\geq 2}。 使用福特-富尔克森算法可找到三条增广路径，分别为{\displaystyle p\_{1}=\{s,v\_{4},v\_{3},v\_{2},v\_{1},t\}}、{\displaystyle p\_{2}=\{s,v\_{2},v\_{3},v\_{4},t\}}、{\displaystyle p\_{3}=\{s,v\_{1},v\_{2},v\_{3},t\}}.

| 步骤 | 增广路径 | 发送流 | 剩余容量 |
| --- | --- | --- | --- |
| e\_{1} | e\_{2} | e\_{3} |
| 0 |  |  | {\displaystyle r^{0}=1} | r | 1 |
| 1 | {\displaystyle \{s,v\_{2},v\_{3},t\}} | 1 | {\displaystyle r^{0}} | {\displaystyle r^{1}} | {\displaystyle 0} |
| 2 | p\_{1} | {\displaystyle r^{1}} | r^{2} | {\displaystyle 0} | {\displaystyle r^{1}} |
| 3 | p\_{2} | {\displaystyle r^{1}} | r^{2} | {\displaystyle r^{1}} | {\displaystyle 0} |
| 4 | p\_{1} | r^{2} | {\displaystyle 0} | {\displaystyle r^{3}} | r^{2} |
| 5 | p\_3 | r^{2} | r^{2} | {\displaystyle r^{3}} | {\displaystyle 0} |

注意在步骤1和步骤5之后,边e\_{1}、e\_{2}、e\_{3}的残留容量都可以表示为{\displaystyle r^{n}}、{\displaystyle r^{n+1}}或{\displaystyle 0}，同时，对于一些特殊的{\displaystyle n\in \mathbb {N} }这意味着算法可以通过p\_{1}、p\_{2}、p\_{1}与 p\_3无限增广，并且残留容量总处于一个循环。在步骤5之后网络的流为{\displaystyle 1+2(r^{1}+r^{2})}，如果继续用以上的算法增广，总的流将向{\displaystyle \textstyle 1+2\sum \_{i=1}^{\infty }r^{i}=3+2r}趋近，但最大流为{\displaystyle 2M+1}。在这个样例中，算法将永远不会停止，且结果也不会向实际的最大流趋近。

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.adj[vertex] = []
 
    def get\_edges(self, v):
        return self.adj[v]
 
    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.adj[u].append(edge)
        self.adj[v].append(redge)
        self.flow[edge] = 0
        self.flow[redge] = 0
 
    def find\_path(self, source, sink, path):
        if source == sink:
            return path
        for edge in self.get\_edges(source):
            residual = edge.capacity - self.flow[edge]
            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.flow[edge] for edge in path]
            flow = min(residuals)
            for edge in path:
                self.flow[edge] += flow
                self.flow[edge.redge] -= flow
            path = self.find\_path(source, sink, [])
        return sum(self.flow[edge] 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

```

应用
--

二分图的最大匹配

最大不相交路径

参考文献
----

1. ↑  Laung-Terng Wang, Yao-Wen Chang, Kwang-Ting (Tim) Cheng.. Morgan Kaufmann. 2009: 204. ISBN 0080922007.
2. ↑  Thomas H. Cormen; Charles E. Leiserson; Ronald L. Rivest; Clifford Stein.. MIT Press. 2009: 714. ISBN 0262258102.
3. ↑  Ford, L. R.; Fulkerson, D. R.. Canadian Journal of Mathematics. 1956, **8**: 399. doi:10.4153/CJM-1956-045-5.
4. ↑  Zwick, Uri.. Theoretical Computer Science. 21 August 1995, **148** (1): 165–170. doi:10.1016/0304-3975(95)00022-O.

* Cormen, Thomas H.; Leiserson, Charles E.; Rivest, Ronald L.; Stein, Clifford..  Second. MIT Press and McGraw–Hill. 2001: 651–664. ISBN 0-262-03293-7.
* George T. Heineman; Gary Pollice; Stanley Selkow.. . Oreilly Media. 2008: 226–250. ISBN 978-0-596-51624-6.
* Jon Kleinberg; Éva Tardos.. . Pearson Education. 2006: 378–384. ISBN 0-321-29535-8.

|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| 

| * 
* 论
* 编
算法 |
| --- |
|  |
| 排序 | 

|  |  |
| --- | --- |
| 比较排序 | 
* 冒泡排序
* 选择排序
* 插入排序
* 希尔排序
* 快速排序
* 归并排序
* 堆排序
* 鸡尾酒排序
* 梳排序
* 侏儒排序
* 图书馆排序
* 内省排序
* 奇偶排序

 |
|  |
| 线性时间排序 | 
* 鸽巢排序
* 基数排序
* 計數排序
* 桶排序

 |
|  |
| 并行排序 | 
* 排序网络（英语：）
* Batcher归并网络

 |
|  |
| 不实用的 | 
* Bogo排序
* 臭皮匠排序

 |
|  |
| 图 | 
* 拓撲排序

 |

 |
|  |
| 搜索 | 

|  |  |
| --- | --- |
| 列表 | 
* 线性搜索
* 二分搜索
* 插值搜尋

 |
|  |
| 树・图 | 
* 广度优先搜索
	+ 最良優先搜索（英语：）
	+ 均一开销搜索
	+ A\*
* 深度优先搜索
	+ 迭代深化深度优先搜索
	+ 深度限制搜索（日语：）
	+ 双向搜索
* 分枝限定法（英语：）

 |
|  |
| 字符串 | 
* KMP算法
* 博耶-穆尔字符串搜索算法
* AC自动机算法
* 拉宾-卡普算法
* bitap算法

 |

 |
|  |
| 最短路问题 | 
* 戴克斯特拉算法
* 贝尔曼-福特算法
* A\*搜尋演算法
* Floyd-Warshall算法

 |
|  |
| 最小生成树 | 
* 普林姆算法
* 克鲁斯克尔演算法

 |
|  |
| 最大流最小割 | 
* 福特-富尔克森算法
* 埃德蒙兹-卡普算法
* 迪尼茨算法

 |
|  |
| 线性规划 | 
* 单纯形法
* 卡马卡尔算法（英语：）

 |
|  |
| 順序統計量 | 
* 选择算法
* 中位数的中位数（英语：）

 |
|  |
| 種類 | 
* 精确算法
* 近似算法
* 随机化算法

 |
|  |
| 其他 | 
* 分治法
* 动态规划
* 贪心算法

 |
|  |
| **Category:算法** |

 |