KM算法略解
匈牙利算法用于解决二分图最优匹配问题。
KM算法用于解决带权二分图最优匹配问题。
不仅要让有情人终成眷属,还得选最好嗑的cp
算法原理
其实我自己也不是很懂,大概是感性理解的(雾)。
推荐看这篇博客,讲的很详细,而且有题目推荐。
后面的<优化>部分,基本上来源于这篇博客。不知道算不算转载,毕竟我写的并没有它那么好。
算法流程
首先,摸出一张带权二分图。
(配图略)
把它变成一张完全图。
(原本不存在的边,权值设为\(-\inf\)即可)
二分图补完计划
假设已经二分好了,左边\(a\)个节点排成一列,右边\(b\)个节点排成一列。
接下来给节点赋初始点权。
对于左边的节点,其权值为连边的最大权值。
对于右边的节点,其权值为\(0\)。
(配图略)
开始进行匹配。匹配类似匈牙利算法,但采取一定的贪心策略(?)进行改良。
具体而言,只通过边权=两端点点权之和的边进行匹配。
当然,会有无法匹配的情况,例如(配图略)。
于是考虑修改目前被匹配过的点的点权,将左节点均\(-1\),右节点均\(+1\)。
重复以上过程,直到找到匹配。
调整点权的过程挺浪费时间的……草履虫都觉得每次修改\(1\)必然会挂飞。
每次-1仿佛刮痧……
-1-1-1-1-1-1-1-1……
毕竟谁家时间复杂度还带个\(\sum w\)啊(雾)。
容易想到,只要使得每次调整点权都是切实有效的,就可以大大减少复杂度。
令\(d_v = \min(lx_u+ly_v-w_{u,v} | vis_u = true \and vis_u = false)\)
又令\(now = \min(d_v | vis_v = false)\)
其中\(lx\)表示左节点的点权,\(ly\)表示右节点的点权,\(vis\)表示是否被访问过。
总之就是,找到一个最小的修改量,使得必然有一个新的右节点被访问到。(也就是接触到了新的匹配方案)
求出\(now\)之后,将所有左节点\(-now\)。
维护\(d\),每次操作后使得\(d_v = d_v-now\),并且如果\(d_v\)在这次操作中变成了\(0\),那么\(v\)就是被访问的新右节点,关于它,可能有两种情况:
- 如果\(v\)没有被匹配。那么退出循环,更新match数组,然后进行下一个左节点的匹配。
- \(v\)已经被匹配。则把\(v\)与\(match_v\)标记为已访问,继续更新\(d\)和\(now\)。
然后还要注意,用bfs代替dfs。否则,如果匹配的部分跑满了,复杂度还是\(O(n^4)\)。
(数据酱的恶意都满溢出来了……)
总复杂度\(O(n^3)\),可喜可贺,可喜可贺。
本文来自博客园,作者:_kilo-meteor,转载请注明原文链接:https://www.cnblogs.com/meteor2008/p/17866363.html