我手头有一个问题,可以简化为以下内容:
假设在一个二维平面 X-Y 上有一堆随机点。对于每个 Y 值,可能有多个点在 X 轴上;对于每个 X 值,可能有多个点在 Y 轴上。
每当选择一个点 (Xi,Yi) 时,不能选择 X = Xi 或 Y = Yi 的任何其他点。我们必须选择最大数量的点。
我手头有一个问题,可以简化为以下内容:
假设在一个二维平面 X-Y 上有一堆随机点。对于每个 Y 值,可能有多个点在 X 轴上;对于每个 X 值,可能有多个点在 Y 轴上。
每当选择一个点 (Xi,Yi) 时,不能选择 X = Xi 或 Y = Yi 的任何其他点。我们必须选择最大数量的点。
此外,还有一篇关于最大流算法的教程
http://www.topcoder.com/tc?module=Static&d1=tutorials&d2=maxFlow
这不就是匈牙利算法吗?
创建一个n×n矩阵,标记顶点为0,未标记顶点为1。该算法将选择n个顶点,每行和每列各选一个,以使它们的总和最小化。只需计算所有等于0的已选顶点数量,即可得出答案。
from munkres import Munkres
matrix = [[0, 0, 1],
[0, 1, 1],
[1, 0, 0]]
m = Munkres()
total = 0
for row, column in m.compute(matrix):
if matrix[row][column] == 0:
print '(%i, %i)' % (row, column)
total += 1
print 'Total: %i' % total
这个算法的时间复杂度为O(n3),其中 n 是矩阵中行的数量。最大流算法的时间复杂度为O(V3),其中 V 是顶点的数量。只要选择的交叉点超过了 n 个,该算法就会运行得更快;事实上,随着选择的顶点数量增加,它的运行速度会以数量级的方式提高。
(1,2)
。(10,2)
。第二轮:
(3,5);(10,5);(10,3)
3,10
3,5
(3,5),(10,5),(10,3),(3,3)
反应2:
(10,3)
。(10,5)
。第三轮:
(3, 5)
反应3:
(3,5)
。(1, 2); (3, 5); (10, 5); (10, 2); (10, 3)
。 - IVladXY平面是一个误导。将其表述为一组元素,每个元素都有一组互斥的元素。
然后算法变成了深度优先搜索。在每个级别上,对于每个候选节点,计算被排除的元素集合,即当前被排除的元素与候选节点排除的元素的并集。按照被排除元素最少到最多的顺序尝试候选节点。跟踪到目前为止的最佳解决方案(被排除节点最少)。修剪任何比当前最佳的子树更差的子树。
为了稍微提高效率,可以使用Bloom过滤器来跟踪被排除的集合,但可能会错过一些解决方案。
基于IVlad的推荐,我研究了Hopcroft-Karp算法。对于这个问题,它通常比最大流算法和匈牙利算法更好,而且往往显著优于它们。以下是一些比较:
一般情况下:
对于一个50×50的矩阵,其中50%的顶点已被选择:
对于一个1000×1000的矩阵,选择10个顶点::
只有在选择的点占比显著较高的情况下,匈牙利算法才更好。
对于一个100×100的矩阵,选择90%的顶点::
最大流算法永远不会更好。
实际上,它也相当简单。这段代码使用了David Eppstein的实现:
points = {
0 : [0, 1],
1 : [0],
2 : [1, 2],
}
selected = bipartiteMatch(points)[0]
for x, y in selected.iteritems():
print '(%i, %i)' % (x, y)
print 'Total: %i' % len(selected)
O(n^3)
的时间复杂度内使用邻接矩阵实现Edmonds-Karp算法。你的测试归结为邻接表与邻接矩阵之间的区别。只是匈牙利算法绝对需要一个矩阵,但其他算法并不绝对需要邻接表(尽管我个人从未见过Hopcroft-Karp算法使用矩阵实现,但我相信它是可以的)。 - IVlad