KM算法的前提是图存在一个完备匹配,因此用于二分图的最佳匹配问题。如果是最大权匹配问题,可以通过加权值为0的边来可以将图的最佳匹配与最大全匹配统一起来;如果是最小权匹配问题,可以通过加权值为-INF的边来可以将图的最佳匹配与最大全匹配统一起来(HDU 1853)。见:完美匹配,完备匹配,最佳匹配,最大权匹配
如果我们要求边权值最小的匹配可以把边权值取负值,得出结果后再取相反数即为答案。
KM算法求得的最大权匹配是边权值和最大,如果想要边权之积最大,每条边权取自然对数,然后求最大和权匹配,求得的结果a再算出e^a就是最大积匹配。
题集:
poj 3565
hdu 2255
hdu 1533
hdu 3488 hdu1853 hdu 3435
hdu 2426
hdu 2853
hdu 3718
hdu 3722
hdu 3395
hdu 2282
hdu 2813
hdu 2448
hdu 2236
hdu 3315
hdu 3523
KM算法各版本模板:
dfs版KM算法模板O(n4)复杂度
图解
图解+模板
对访问过且匹配过的男生,期望值要加上女生减去的期望值;代码中的未访问过的男生实际上包含两种,一种是完全没被访问过的(slack[ j ] = INF),一种是访问过,且更新过slack值但没有参与匹配的男生,由于这种男生可匹配的部分女生期望值降低,所以该男生的slack值减少。
HDU 2255 模板
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
const int MAXN = 305;
const int INF = 0x3f3f3f3f;
// 女生视为左图点,男生视为右图点
int love[MAXN][MAXN]; // 记录每个妹子和每个男生的好感度
int ex_girl[MAXN]; // 每个妹子的期望值
int ex_boy[MAXN]; // 每个男生的期望值
bool vis_girl[MAXN]; // 记录每一轮匹配匹配过的女生
bool vis_boy[MAXN]; // 记录每一轮匹配匹配过的男生
int match[MAXN]; // 记录每个男生匹配到的妹子 如果没有则为-1
int slack[MAXN]; // 记录每个汉子如果能被妹子倾心最少还需要多少期望值
int N;
bool dfs(int girl)
{
vis_girl[girl] = true;
for (int boy = 0; boy < N; ++boy) {
if (vis_boy[boy]) continue; // 每一轮匹配 每个男生只尝试一次
int gap = ex_girl[girl] + ex_boy[boy] - love[girl][boy];
if (gap == 0) {
// 如果符合要求
vis_boy[boy] = true;
if (match[boy] == -1 || dfs( match[boy] )) {
// 找到一个没有匹配的男生 或者该男生的妹子可以找到其他人
match[boy] = girl;
return true;
}
} else {
slack[boy] = min(slack[boy], gap); // slack 可以理解为该男生要得到女生的倾心 还需多少期望值 取最小值 备胎的样子【捂脸
}
}
return false;
}
int KM()
{
memset(match, -1, sizeof match); // 初始每个男生都没有匹配的女生
memset(ex_boy, 0, sizeof ex_boy); // 初始每个男生的期望值为0
// 每个女生的初始期望值是与她相连的男生最大的好感度
for (int i = 0; i < N; ++i) {
ex_girl[i] = love[i][0];
for (int j = 1; j < N; ++j) {
ex_girl[i] = max(ex_girl[i], love[i][j]);
}
}
// 尝试为每一个女生解决归宿问题
for (int i = 0; i < N; ++i) {
fill(slack, slack + N, INF); // 因为要取最小值 初始化为无穷大
while (1) {
// 为每个女生解决归宿问题的方法是 :如果找不到就降低期望值,直到找到为止
// 记录每轮匹配中男生女生是否被尝试匹配过
memset(vis_girl, false, sizeof vis_girl);
memset(vis_boy, false, size