匈牙利算法

匈牙利算法是一种在多项式时间内求解任务分配问题的组合优化算法，并推动了后来的。美国数学家哈罗德·W·库恩于1955年提出该算法。此算法之所以被称作匈牙利算法，是因为算法很大一部分是基于以前匈牙利数学家和艾盖瓦里·耶内的工作之上创建起来的。
詹姆士·芒克勒斯在1957年回顾了该算法，并发现它的时间复杂度为（强）多项式时间。 此后该算法被称为库恩-芒克勒斯算法或芒克勒斯分配算法。原始算法的时间复杂度为formula_1，但与理查德·卡普发现可以修改算法达到formula_2运行时间。小莱斯特·伦道夫·福特和德尔伯特·雷·富尔克森将该方法推广到一般运输问题，称作福特-富尔克森算法。2006年发现卡尔·雅可比在19世纪就解决了指派问题，该解法在他死后在1890年以拉丁文发表。
问题.
你有三个工人：吉姆，史提夫和艾伦。
你需要其中一个清洁浴室，另一个打扫地板，第三个洗窗，但他们每个人对各项任务要求不同数目数量的钱。
以最低成本的分配工作的方式是什么？
可以用工人做工的成本矩阵来表示该问题。例如：
当把匈牙利方法应用于上面的表格时，会给出最低成本：为6美元，让吉姆清洁浴室、史提夫打扫地板、艾伦清洗窗户就可以达到这一结果。
设定.
给定一个 formula_3 的非负矩阵，其中第 formula_4 行第 formula_5 列元素表示把第 formula_4 个工人派到第 formula_5个工作的成本。我们必须找到成本最低的工人工作分配。如果目标是找到最高成本的分配，该问题可以将每个成本都换为最高一个成本减去该成本以适应题目。
如果用二分图来阐述该问题可以更容易描述这个算法。对于一个有 formula_8个工人节点（formula_9）与 formula_8个工作节点（formula_11）的完全二分图 formula_12，每条边都有 formula_13 的非负成本。我们要找到最低成本的完美匹配。
如果函数 formula_14 满足对于每个 formula_15 都有 formula_16，则把该函数叫做势。势 formula_17 的值为 formula_18。可以看出，每个完美匹配的成本最低是每个势的值。匈牙利算法找出了完美匹配及与之成本/值相等的势，这证明了两者的最优性。实际上它找到了紧边集的完美匹配：紧边 formula_19 是指对于势 formula_17 满足 formula_21。我们将紧边子图表示为 formula_22。formula_22 中的完美匹配的成本（如果存在）就等于 formula_17的值。
用二分图描述此算法.
在算法中我们维持 formula_22 的势formula_17和方向（表示为 formula_27 ），该方向有从 formula_11到 formula_9 的边构成匹配 formula_30 的性质。初始时，formula_17 处处为 0，
所有边都由 formula_9 指向 formula_11（因此 formula_30 为空）。每一步中，我们或者改变 formula_17 使其值增加，
或者改变方向以得到更多的边。我们保持 formula_30 的所有边都是紧边不发生改变。当 formula_30 为完美匹配时结束。
在一般的步骤中，令 formula_38 和 formula_39 为 formula_30 未覆盖的节点
（则 formula_41 包含 formula_9 中入度为零的节点，而 formula_43 中包含 formula_11 中出度为零的节点）。
令 formula_45 为从 formula_41 只沿紧边的有向路径可到达 formula_27 的节点。可由广度优先搜索求得。
若 formula_48 非空，则将 formula_27 中从 formula_41 到 formula_43 的有向路径反向。则相应匹配数增加1。
若 formula_48 为空，则令 formula_53。formula_54 为正，因为 formula_55 与 formula_56 之间没有紧边。
在 formula_57 中的节点将formula_17增加formula_54 并在 formula_55 中节点将 formula_17减小 formula_54，
得到的formula_17仍然是势。图 formula_22 改变了，但它仍包含formula_30。我们把新的边从formula_9指向formula_11。
由 formula_54 的定义，formula_41 可达的节点集 formula_70 增大（注意到紧边的数量不一定增加）。
我们重复这些步骤直到formula_30为完美匹配，该情形下给出的是最小成本（即时间消耗）的匹配。此版本的运行时间为 formula_1：formula_30 增广 formula_8次，
在 formula_30 不改变的一个阶段中，势最多改变 formula_8 次（因为 formula_70 每次都增加）。改变势所需的时间在 formula_78。
证明：改变势函数"y"不改变"M".
证明改变formula_79后formula_80中每条边不发生改变，这等价于证明对于formula_80中任意边，它的两个顶点要么都在formula_45中，要么都不在formula_45中。为此，定义formula_84为formula_80中一条从formula_86到formula_87的边，则若formula_88，那么有formula_89。（反证）假设formula_90但formula_91；由于formula_92是一个匹配边的末端点，formula_93，因此存在从formula_41到formula_92的有向路径。这条路径必不能经过formula_96（根据假设），因此这条路径上紧邻formula_92的点是其他点formula_98。formula_99是一条从formula_86到formula_87的紧边因此也是formula_80中的一个元素。但因此formula_80包含两条有共点的边，与formula_80是匹配的定义矛盾。因此formula_80中每条边的两个顶点要么都在formula_45中，要么都不在formula_45中。
证明："y"仍然是势函数.
证明formula_79在更改之后是势函数，等价于证明不存在总势超过成本的边。这已经在之前的论述中已经为formula_80中的边建立这个概念，因此我们考虑任意从formula_87到formula_86的边formula_112。如果formula_113升高了formula_54，那么：(1)formula_115，在这种情况下，formula_116减小了formula_54，使总体的势未发生改变；(2)formula_118，这种情况下formula_54的定义保证了formula_120。因此formula_79仍然是势函数。
矩阵解释.
给定 formula_122 个工人和任务，以及一个包含分配给每个工人一个任务的成本的 formula_123矩阵，寻找成本最小化分配。
首先把问题写成下面的矩阵形式
其中 formula_125是执行任务 1, 2, 3, 4 的工人。formula_126 分别表示当工人 formula_127 做任务 1, 2, 3, 4 时的时间损失（成本）。对于其他符号也同样适用。该矩阵是方阵，所以每个工人只能执行一个任务。
第 1 步
接下来我们对矩阵的行进行操作。将所有 formula_128 （formula_4 从 1 到 4）中最小的元素取走，并将该行每个元素都减去刚刚取走的元素。这会让该行至少出现一个零（当一行有两个相等的最小元素时会得到多个零）。对此过程为所有行重复。我们现在得到一个每行至少有一个零的矩阵。现在我们尝试给工人指派任务，以使每个工人只做一项任务，并且每个情况的耗散都为零。说明如下。
每一行的 0 的个数可以超过 1。
若有出现行中 0 的个数超过 1，在最坏的情况下，会需要在 formula_130 的组合中找到一个成本最小的配对。
第 2 步
有时此阶段的该矩阵不能符合指派的要求，例如下面所示矩阵。
在上述情形下，不能做出指派。注意到任务 1 由工人 formula_131 和 formula_132 做都很高效 (formula_133 和 formula_134 为 0 )。但两个工人无法分配到同一个任务。
还注意到，没有任何一个工人能有效地做任务 3 (formula_135、formula_136、formula_137、formula_138 皆不为 0)。
为了克服这个问题，我们对所有列重复上述流程（即每一列所有元素都减去该列最小元素）并检查是否可以完成分配。
大多数情况下，这都会给出结果，但如果仍然是不可行，那么我们需要继续下去。
第 3 步
必须用尽可能少的列或行标记来覆盖矩阵中的所有零。下面的过程是完成这个要求的一种方法：
首先，尽可能多地分配任务，用 0' 表示的零为已指派的任务，
现在到了画图的部分。
现在划掉所有已标记的列和未标记的行（第 1 列和第 2, 4 行）。
上述详细的描述只是画出覆盖所有 0 的（直、行）线的一种方法。也可以使用其他方法。
第 4 步
现在删除已画线的行和列。这将留下一个矩阵如下：
返回到步骤 1，然后重复这个过程，直到矩阵是空的；当矩阵为空时，意即画出的覆盖所有 0 的（直、行）线的个数等于原本矩阵的行数(或列数) formula_122，在上述的例子中 formula_141，此时代表有一个分配的最佳解存在于这些 0 的组合之中，我们可以在步骤 1 中从所有分配的组合中找到成本最小的解。
外部链接.
实现.
（请注意，并非所有这些都满足 formula_2 时间约束。）