**匈牙利算法**是一种在多项式时间内求解任务分配问题的组合优化算法，并推动了后来的原始对偶方法（英语：）。美国数学家哈罗德·W·库恩于1955年提出该算法。此算法之所以被称作匈牙利算法，是因为算法很大一部分是基于以前匈牙利数学家科尼格·德內什（英语：）和艾蓋瓦里·耶內的工作之上创建起来的。

詹姆士·芒克勒斯在1957年回顾了该算法，并发现它的时间复杂度为（强）多项式时间。 此后该算法被称为**库恩-芒克勒斯算法**或**芒克勒斯分配算法**。原始算法的时间复杂度为O(n^4)，但杰克·爱德蒙斯（英语：）与理查德·卡普发现可以修改算法达到O(n^{3})运行时间。小萊斯特·倫道夫·福特和德爾伯特·雷·富爾克森将该方法推广到一般运输问题，稱作福特-富爾克森算法。2006年发现卡爾·雅可比在19世纪就解决了指派问题，该解法在他死后在1890年以拉丁文发表。

問題
--

你有三个工人：吉姆，史提夫和艾伦。
你需要其中一个清洁浴室，另一个打扫地板，第三个洗窗，但他们每个人对各项任务要求不同数目数量的钱。
以最低成本的分配工作的方式是什么？
可以用工人做工的成本矩阵来表示该问题。例如：

|  | 清洁浴室 | 打扫地板 | 洗窗 |
| --- | --- | --- | --- |
| 吉姆 | $2 | $3 | $3 |
| 史提夫 | $3 | $2 | $3 |
| 艾伦 | $3 | $3 | $2 |

当把匈牙利方法应用于上面的表格时，会给出最低成本：为6美元，让吉姆清洁浴室、史提夫打扫地板、艾伦清洗窗户就可以达到这一结果。

设定
--

给定一个 {\displaystyle n\times n} 的非负矩阵，其中第 i 行第  j  列元素表示把第 i 个工人派到第  j 个工作的成本。我们必须找到成本最低的工人工作分配。如果目标是找到最高成本的分配，该问题可以将每个成本都换为最高一个成本减去该成本以适应题目。

如果用二分图来阐述该问题可以更容易描述这个算法。对于一个有 n个工人节点（S）与 n个工作节点（T）的完全二分图 {\displaystyle G=(S\cup T,E)}，每条边都有 {\displaystyle c(i,j)} 的非负成本。我们要找到最低成本的完美匹配。

如果函数 y: (S \cup T) \mapsto \mathbb{R} 满足对于每个 i \in S, j \in T 都有 y(i)+y(j) \leq c(i, j)，则把该函数叫做**势**。势  y  的值为 \sum\_{v\in S\cup T} y(v)。可以看出，每个完美匹配的成本最低是每个势的值。匈牙利算法找出了完美匹配及与之成本/值相等的势，这证明了两者的最优性。实际上它找到了**紧边集**的完美匹配：紧边 {\displaystyle ij} 是指对于势  y  满足 y(i)+y(j) = c(i, j)。我们将紧边子图表示为 G\_y。G\_y 中的完美匹配的成本（如果存在）就等于  y 的值。

用二分图描述此算法
---------

在算法中我们維持 G\_y 的势 y 和方向（表示为 \overrightarrow{G\_y} ），该方向有从 T到 S 的边构成匹配 M 的性质。初始时， y  处处为 0，
所有边都由 S 指向 T（因此 M 为空）。每一步中，我们或者改变  y  使其值增加，
或者改变方向以得到更多的边。我们保持 M 的所有边都是紧边不发生改变。当 M 为完美匹配时结束。

在一般的步骤中，令 R\_S \subseteq S 和 R\_T \subseteq T 为 M 未覆盖的节点
（则 R\_{S} 包含 S 中入度为零的节点，而 R\_{T} 中包含 T 中出度为零的节点）。
令 Z 为从 R\_{S} 只沿紧边的有向路径可到达 \overrightarrow{G\_y} 的节点。可由广度优先搜索求得。

若 R\_T \cap Z 非空，则将 \overrightarrow{G\_y} 中从 R\_{S} 到 R\_{T} 的有向路径反向。则相应匹配数增加1。

若 R\_T \cap Z 为空，则令 \Delta := \min \{c(i,j)-y(i)-y(j): i \in Z \cap S, j \in T \setminus Z\}。\Delta  为正，因为 Z \cap S 与 T \setminus Z 之间没有紧边。
在 Z \cap T 中的节点将 y 增加\Delta  并在 Z \cap S 中节点将  y 减小 \Delta ，
得到的 y 仍然是势。图 G\_y 改变了，但它仍包含M。我们把新的边从S指向T。
由 \Delta  的定义，R\_{S} 可达的节点集 Z 增大（注意到紧边的数量不一定增加）。

我们重复这些步骤直到M为完美匹配，该情形下给出的是最小成本（即时间消耗）的匹配。此版本的运行时间为 O(n^4)：M 增广 n次，
在 M 不改变的一个阶段中，势最多改变 n 次（因为 Z 每次都增加）。改变势所需的时间在 O(n^{2})。

### 证明：改变势函数*y*不改变*M*

证明改变y后M中每条边不发生改变，这等价于证明对于M中任意边，它的两个顶点要么都在Z中，要么都不在Z中。为此，定义{\displaystyle vu}为M中一条从T到S的边，则若{\displaystyle v\in Z}，那么有{\displaystyle u\in Z}。（反证）假设{\displaystyle u\in Z}但{\displaystyle v\notin Z}；由于u是一个匹配边的末端点，{\displaystyle u\notin R\_{S}}，因此存在从R\_{S}到u的有向路径。这条路径必不能经过v（根据假设），因此这条路径上紧邻u的点是其他点{\displaystyle v'\in T}。{\displaystyle v'u}是一条从T到S的紧边因此也是M中的一个元素。但因此M包含两条有共点的边，与M是匹配的定义矛盾。因此M中每条边的两个顶点要么都在Z中，要么都不在Z中。

### 证明：*y*仍然是势函数

证明y在更改之后是势函数，等价于证明不存在总势超过成本的边。这已经在之前的论述中已经为M中的边建立这个概念，因此我们考虑任意从S到T的边uv。如果y(u)升高了\Delta ，那么：(1){\displaystyle v\in Z\cap T}，在这种情况下，{\displaystyle y(v)}减小了\Delta ，使总体的势未发生改变；(2){\displaystyle v\in T\setminus Z}，这种情况下\Delta 的定义保证了{\displaystyle y(u)+y(v)+\Delta \leq c(u,v)}。因此y仍然是势函数。

矩阵解释
----

给定 n 个工人和任务，以及一个包含分配给每个工人一个任务的成本的 {\displaystyle n\times n}矩阵，寻找成本最小化分配。

首先把问题写成下面的矩阵形式

{\displaystyle {\begin{bmatrix}a\_{1}&a\_{2}&a\_{3}&a\_{4}\\b\_{1}&b\_{2}&b\_{3}&b\_{4}\\c\_{1}&c\_{2}&c\_{3}&c\_{4}\\d\_{1}&d\_{2}&d\_{3}&d\_{4}\end{bmatrix}}}
其中 {\displaystyle a,b,c,d}是执行任务 1, 2, 3, 4 的工人。{\displaystyle a\_{1},a\_{2},a\_{3},a\_{4}} 分别表示当工人 a 做任务 1, 2, 3, 4 时的时间损失（成本）。对于其他符号也同样适用。该矩阵是方阵，所以每个工人只能执行一个任务。

**第 1 步**

接下来我们对矩阵的行进行操作。将所有 a\_{i} （i 从 1 到 4）中最小的元素取走，并将该行每个元素都减去刚刚取走的元素。这会让该行至少出现一个零（当一行有两个相等的最小元素时会得到多个零）。对此过程为所有行重复。我们现在得到一个每行至少有一个零的矩阵。现在我们尝试给工人指派任务，以使每个工人只做一项任务，并且每个情况的耗散都为零。说明如下。

|  |  |  |  |
| --- | --- | --- | --- |
| {\displaystyle 0} | {\displaystyle a\_{2}'} | {\displaystyle a\_{3}'} | {\displaystyle a\_{4}'} |
| {\displaystyle b\_{1}'} | {\displaystyle b\_{2}'} | {\displaystyle b\_{3}'} | {\displaystyle 0} |
| {\displaystyle c\_{1}'} | {\displaystyle 0 } | {\displaystyle c\_{3}'} | {\displaystyle c\_{4}'} |
| {\displaystyle d\_{1}'} | {\displaystyle d\_{2}'} | {\displaystyle 0} | {\displaystyle d\_{4}'} |

每一行的 0 的个数可以超过 1。

若有出现行中 0 的个数超过 1，在最坏的情况下，会需要在 n! 的组合中找到一个成本最小的配对。

**第 2 步**

有时此阶段的该矩阵不能符合指派的要求，例如下面所示矩阵。

|  |  |  |  |
| --- | --- | --- | --- |
| {\displaystyle 0} | {\displaystyle a\_{2}'} | {\displaystyle a\_{3}'} | {\displaystyle a\_{4}'} |
| {\displaystyle b\_{1}'} | {\displaystyle b\_{2}'} | {\displaystyle b\_{3}'} | {\displaystyle 0} |
| {\displaystyle 0} | {\displaystyle c\_{2}'} | {\displaystyle c\_{3}'} | {\displaystyle c\_{4}'} |
| {\displaystyle d\_{1}'} | {\displaystyle 0} | {\displaystyle d\_{3}'} | {\displaystyle d\_{4}'} |

在上述情形下，不能做出指派。注意到任务 1 由工人 a 和 c 做都很高效 ({\displaystyle a\_{1}'} 和 {\displaystyle c\_{1}'} 为 0 )。但两个工人无法分配到同一个任务。

还注意到，没有任何一个工人能有效地做任务 3 ({\displaystyle a\_{3}'}、{\displaystyle b\_{3}'}、{\displaystyle c\_{3}'}、{\displaystyle d\_{3}'} 皆不为 0)。
为了克服这个问题，我们对所有列重复上述流程（即每一列所有元素都减去该列最小元素）并检查是否可以完成分配。

大多数情况下，这都会给出结果，但如果仍然是不可行，那么我们需要继续下去。

**第 3 步**

必须用尽可能少的列或行标记来覆盖矩阵中的所有零。下面的过程是完成这个要求的一种方法：

首先，尽可能多地分配任务，用 0' 表示的零为已指派的任务，

* 第 1 行有一个零，所以用 0' 表示为分配。第 3 行的 0 由于处于同一列而被划掉。
* 第 2 行有一个零，所以用 0' 表示为分配。
* 第 3 行只有一个已经划掉的零，所以不能分配。
* 第 4 行有两个未划掉的零。可以分配任何一个（都是最优） ，并将另一个零划去。

(在此阶断任何合法的分配都可以接受，因此若一开始分配的是第 3 行的 0，就会使第 1 行的 0 被划掉。)

|  |  |  |  |
| --- | --- | --- | --- |
| 0' | {\displaystyle a\_{2}'} | {\displaystyle a\_{3}'} | {\displaystyle a\_{4}'} |
| {\displaystyle b\_{1}'} | {\displaystyle b\_{2}'} | {\displaystyle b\_{3}'} | 0' |
| {\displaystyle 0} | {\displaystyle c\_{2}'} | {\displaystyle c\_{3}'} | {\displaystyle c\_{4}'} |
| {\displaystyle d\_{1}'} | 0' | {\displaystyle 0} | {\displaystyle d\_{4}'} |

现在到了画图的部分。

* 标记所有未分配的行（第 3 行）。
* 标记所有新标记的行中 0所在（且未标记）的對應列（第 1 列）。
* 标记所有在新标记的列中有分配的行（第 1 行）。
* 对所有未分配的行重复上述过程。


|  |  |  |  |  |
| --- | --- | --- | --- | --- |
| × |  |  |  |  |
| 0' | {\displaystyle a\_{2}'} | {\displaystyle a\_{3}'} | {\displaystyle a\_{4}'} | × |
| {\displaystyle b\_{1}'} | {\displaystyle b\_{2}'} | {\displaystyle b\_{3}'} | 0' |  |
| {\displaystyle 0} | {\displaystyle c\_{2}'} | {\displaystyle c\_{3}'} | {\displaystyle c\_{4}'} | × |
| {\displaystyle d\_{1}'} | 0' | {\displaystyle 0} | {\displaystyle d\_{4}'} |  |

现在劃掉所有已标记的列和**未标记**的行（第 1 列和第 2, 4 行）。

|  |  |  |  |  |
| --- | --- | --- | --- | --- |
| × |  |  |  |  |
| 0' | {\displaystyle a\_{2}'} | {\displaystyle a\_{3}'} | {\displaystyle a\_{4}'} | × |
| {\displaystyle b\_{1}'} | {\displaystyle b\_{2}'} | {\displaystyle b\_{3}'} | 0' |
| {\displaystyle 0} | {\displaystyle c\_{2}'} | {\displaystyle c\_{3}'} | {\displaystyle c\_{4}'} | × |
| {\displaystyle d\_{1}'} | 0' | {\displaystyle 0} | {\displaystyle d\_{4}'} |

上述详细的描述只是画出覆盖所有 0 的（直、行）线的一种方法。也可以使用其他方法。

**第 4 步**

现在删除已畫線的行和列。这将留下一个矩阵如下：

{\displaystyle {\begin{bmatrix}a\_{2}&a\_{3}&a\_{4}\\c\_{2}&c\_{3}&c\_{4}\end{bmatrix}}}
返回到步骤 1，然后重复这个过程，直到矩阵是空的；当矩阵为空时，意即画出的覆盖所有 0 的（直、行）线的个数等于原本矩阵的行数(或列数) n，在上述的例子中 n=4，此时代表有一个分配的最佳解存在于这些 0 的组合之中，我们可以在步骤 1 中从所有分配的组合中找到成本最小的解。

参考书目
----

* R.E. Burkard, M. Dell'Amico, S. Martello: *Assignment Problems* (Revised reprint). SIAM, Philadelphia (PA.) 2012. ISBN 978-1-61197-222-1
* M. Fischetti, "Lezioni di Ricerca Operativa", Edizioni Libreria Progetto Padova, Italia, 1995.
* R. Ahuja, T. Magnanti, J. Orlin, "Network Flows", Prentice Hall, 1993.
* S. Martello, "Jeno Egerváry: from the origins of the Hungarian algorithm to satellite communication". Central European Journal of Operations Research 18, 47–58, 2010

参考文献
----

1. ↑  Harold W. Kuhn, "The Hungarian Method for the assignment problem", *Naval Research Logistics Quarterly*, **2**: 83–97, 1955. Kuhn's original publication.
2. ↑  Harold W. Kuhn, "Variants of the Hungarian method for assignment problems", *Naval Research Logistics Quarterly*, **3**: 253–258, 1956.
3. ↑  J. Munkres, "Algorithms for the Assignment and Transportation Problems", *Journal of the Society for Industrial and Applied Mathematics*, **5**(1):32–38, 1957 March.
4. ↑ .  [2015-08-14]. （原始内容存档于2020-01-29）.

外部链接
----

* Bruff, Derek, "The Assignment Problem and the Hungarian Method",  （页面存档备份，存于）
* Mordecai J. Golin, Bipartite Matching and the Hungarian Method, Course Notes, Hong Kong University of Science and Technology.
* R. A. Pilgrim, *Munkres' Assignment Algorithm. Modified for Rectangular Matrices （页面存档备份，存于）*, Course notes, Murray State University.
* Mike Dawes, *The Optimal Assignment Problem*, Course notes, University of Western Ontario.
* On Kuhn's Hungarian Method – A tribute from Hungary （页面存档备份，存于）, András Frank, Egervary Research Group, Pazmany P. setany 1/C, H1117, Budapest, Hungary.
* Lecture: Fundamentals of Operations Research - Assignment Problem - Hungarian Algorithm （页面存档备份，存于）, Prof. G. Srinivasan, Department of Management Studies, IIT Madras.
* Extension: Assignment sensitivity analysis (with O(n^4) time complexity) （页面存档备份，存于）, Liu, Shell.
* Solve any Assignment Problem online （页面存档备份，存于）, provides a step by step explanation of the Hungarian Algorithm.

### 实现

（请注意，并非所有这些都满足 O(n^{3}) 时间约束。）

* C implementation with O(n^{3}) time complexity （页面存档备份，存于）
* Java implementation of O(n^{3}) time variant （页面存档备份，存于）
* Python implementation （页面存档备份，存于） (see also here)
* Ruby implementation with unit tests （页面存档备份，存于）
* C# implementation （页面存档备份，存于）
* D implementation with unit tests (port of the Java O(n^{3}) version) （页面存档备份，存于）
* Online interactive implementation （页面存档备份，存于） Please note that this implements a variant of the algorithm as described above.
* Graphical implementation with options (Java applet)
* Serial and parallel implementations. （页面存档备份，存于）
* Implementation in Matlab and C （页面存档备份，存于）
* Perl implementation
* Lisp implementation
* C++ (STL) implementation (multi-functional bipartite graph version) （页面存档备份，存于）
* C++ implementation （页面存档备份，存于）
* C++ implementation of the O(n^{3}) algorithm （页面存档备份，存于） (BSD style open source licensed)
* Another C++ implementation with unit tests （页面存档备份，存于）
* Another Java implementation with JUnit tests (Apache 2.0)[永久]
* MATLAB implementation
* C implementation （页面存档备份，存于）
* Javascript implementation （页面存档备份，存于）
* The clue R package proposes an implementation, solve\_LSAP （页面存档备份，存于）

| 图与树搜索算法 |
| --- |
| * α–β
* A\*
* B\*（英语：）
* 回溯
* 集束（英语：）
* 贝尔曼-福特
* 最佳优先（英语：）
* 双向
* Borůvka（英语：）
* 分支限界
* BFS
* 大英博物馆
* D\*（英语：）
* DFS
* 深度限制（英语：）
* 迪杰斯特拉
* Edmonds（英语：）
* 弗洛伊德
* 边缘搜索
* 爬山
* IDA\*（英语：）
* 迭代加深
* Johnson（英语：）
* 跳点（英语：）
* 克鲁斯克尔
* 词典BFS（英语：）
* LPA\*（英语：）
* 普里姆
* SMA\*（英语：）
* 最短路径快速
 |
| 
分类 |
| * 图算法
* 搜索算法
* 算法列表（英语：）
 |
| 
相关主题 |
| * 动态规划
* 图的遍历
* 树的遍历
 |
| * 
* 论
* 编
 |