匈牙利算法
匈牙利算法
毕设里的一个子问题是求解二分图最大权匹配,网上搜了不少资料最终决定使用KM算法。KM算法的基础是求解二分图最大匹配的匈牙利算法,现在来整理一下这几天所学。
基础概念
- 二分图:如果把一个图的顶点集划分成两个不相交集\(X\)和\(Y\),使得图里的每一条边都分别连接\(X\)和\(Y\)里的顶点,这样的图就叫做二分图。
- 匹配:图的匹配是边集的一个子集,里面的任意两条边都没有公共的顶点。
- 最大匹配:在一个图的所有匹配里,边数最多的匹配称为最大匹配。
- 完备匹配和完美匹配:如果一个图的某个匹配里,被划分的两个点集\(X\)和\(Y\)中有一个点集里的所有点都是匹配点,就称这个匹配是完备匹配。如果两个点集里都是匹配点,就称这个匹配是完美匹配。
- 交替路:从一个未匹配点出发,经过匹配点、未匹配点、匹配点如此重复下去形成的一条路径,称为交替路。
- 增广路:起点终点都是未匹配点的交替路称为增广路。
增广路
- 增广路一定由奇数条边组成
- 增广路中未匹配边一定比匹配边多一条
这两条结论非常显然:增广路的两端都是未匹配点,两端的第一段路径都是未匹配边,中间又必须是匹配边和未匹配边重复偶数次。
仔细思考这两条结论就会发现:增广路通过一次“取反”操作(让匹配边和未匹配边的身份调换)就能让匹配边数+1。
算法的思路
有了上面的观察,就能够利用这个思想设计出求解最大匹配的算法了:
从\(X\)中取一个未匹配点\(x_1\),然后在\(Y\)里取一个与之相连的点\(y_1\),如果\(y_1\)还未被匹配,那直接匹配就行了;
如果\(y_1\)已经被匹配了,接下来的两种策略分别对应了DFS和BFS
- 不放过\(y_1\),回到\(X\)里找到和它匹配的点\(x_2\),直接强迫\(x_2\)另寻匹配点,把\(y_1\)让出来给\(x_1\)
- 放过\(y_1\),在\(Y\)里找下一个和它相连的点\(y_2\),看能否匹配,直到\(Y\)里真的没有能匹配的点了,才强迫\(x_2\)另寻匹配点 ,\(x_1\)自己匹配\(y_1\)
那这跟增广路有什么关系呢?仔细推敲一下就能领悟其中的奥秘所在。增广路的“取反”操作与算法里的“强迫另寻匹配点”正是对应着的。换言之,\(x_1\)找到了一个未匹配点,或者从\(x_1\)找到了一条增广路,都能够使匹配边数+1.
算法的实现
(最近手生且毕设DDL迫在眉睫,BFS版本暂时先鸽一下~)
DFS版本
为简单起见就直接用邻接矩阵来存储图
1 | int Graph[maxm][maxn]; // 邻接矩阵存储图 |
下面是核心的DFS代码
1 | bool findPath(int u) // 为集合X里的结点u寻找匹配点 |
然后是匈牙利算法的代码
1 | void Hungary() |
运行Hungary函数之后就能够求出最大匹配数,存储在match数组里的最后结果正是匹配的结果。
算法正确性
(这里也暂时鸽一下)
参考文献
看了不少博客,这里列出几个我认为讲解的比较好的,感谢各位大神
https://www.cnblogs.com/zpfbuaa/p/7218607.html#_label1
https://www.cnblogs.com/shenben/p/5573788.html
相关文章