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\)就是被访问的新右节点,关于它,可能有两种情况:

  1. 如果\(v\)没有被匹配。那么退出循环,更新match数组,然后进行下一个左节点的匹配。
  2. \(v\)已经被匹配。则把\(v\)\(match_v\)标记为已访问,继续更新\(d\)\(now\)


然后还要注意,用bfs代替dfs。否则,如果匹配的部分跑满了,复杂度还是\(O(n^4)\)
(数据酱的恶意都满溢出来了……)
总复杂度\(O(n^3)\),可喜可贺,可喜可贺。

posted @ 2023-11-30 09:01  _kilo-meteor  阅读(31)  评论(0)    收藏  举报