KM 算法
二分图的最大权完美匹配
不妨先看一些定义:
-
顶标
全称为“顶点标记值”。记左部点
的顶标为 ,右部点 的顶标为 ,那么要求顶标要满足 ,其中 表示 到 那条边的边权。 -
相等子图
即原图中满足
的边构成的子图。- 结论:若相等子图中存在完美匹配,则这个完美匹配就是二分图的带权最大完美匹配。
- 证明:完美匹配的边权和为
。由于 , ,故而整张二分图中,不存在有其他匹配的边权值和大于当前匹配的边权之和。
我们利用上述结论,成功的把问题转换为了:求一组合适的顶标,使得相等子图存在完美匹配。
有一种做法就是对于每一个
-
不妨宏观匈牙利算法来明确一些性质:每次从未匹配边开始,以未匹配边、匹配边交错的形式形成一颗交错树,其中左部节点沿非匹配边访问到右部节点,右部节点沿匹配边找到原来匹配的左部节点。且未找到增广路之前,不会改变已有的匹配。
假设目前的相等子图里找不到增广路了,我们需要一种调整顶标的方法,使得原图中存在的边现在有,且至少一条原来没有的边新图有了。
-
调整方法:我们还是设左部节点为
,右部节点为 。之后把所有的所有访问到过的 加上 、 减去 。进一步转化为如何求一个合适的
,使得满足上述条件。因为左部节点的访问是被动的(即被右部节点沿匹配边访问到),且考虑
没有意义( 的顶标都没有变化),所以我们只用考虑 ,且只用考虑有连边的 。分情况来讨论:
。
那么连接两点的这条边显然是匹配边,那么两边一个加
,一个减 ,显然 ,依旧是新相等子图中的边。 。
在所有访问过的
加 , 减 之后满足顶标的性质—— ,当且仅当 。若在所有的 中取最小的值作为 的值,那么就能达成“既满足顶标的性质,又能至少有一条边加入新相等子图”。
具体的,有以下步骤:
-
枚举点
思想在于不断通过调整顶标加入边以使得
能找到匹配。故重复以下过程直至
找到匹配。-
进行寻找增广路同时获得合适的
。时间复杂度
。 -
调整顶标。
时间复杂度
。
-
时间复杂度
#include <bits/stdc++.h>
#define FL(i, a, b) for(int i = (a); i <= (b); i++)
#define FR(i, a, b) for(int i = (a); i >= (b); i--)
using namespace std;
typedef long long ll;
const int N = 510; const ll INF = 1e17;
int n, m, vis[N], pre[N], match[N];
ll lx[N], ly[N], w[N][N], slack[N];
void aug(int s){
int p, id = 0, q; ll v; match[0] = s;
FL(i, 1, n) slack[i] = INF, pre[i] = 0;
while(match[q]){
p = match[q = id], vis[q] = 1, v = INF;
FL(i, 1, n) if(!vis[i]){
if(slack[i] > lx[p] + ly[i] - w[p][i])
slack[i] = lx[p] + ly[i] - w[p][i], pre[i] = q;
if(slack[i] < v) v = slack[id = i];
}
FL(i, 0, n){
if(vis[i]) lx[match[i]] -= v, ly[i] += v;
else slack[i] -= v;
}
}
for(; q; q = pre[q]) match[q] = match[pre[q]];
}
ll KM(){
FL(i, 1, n){
lx[i] = -INF, ly[i] = 0;
FL(j, 1, n) lx[i] = max(lx[i], w[i][j]);
}
FL(i, 1, n) memset(vis, 0, sizeof(vis)), aug(i);
ll ret = 0; FL(i, 1, n) ret += lx[i] + ly[i];
return ret;
}
int main(){
scanf("%d%d", &n, &m);
FL(i, 1, n) FL(j, 1, n) w[i][j] = -INF;
FL(i, 1, m){
int u, v; ll c;
scanf("%d%d%lld", &u, &v, &c);
w[u][v] = max(w[u][v], c);
}
printf("%lld\n", KM());
FL(i, 1, n) printf("%d ", match[i]);
return 0;
}
二分图的最大权匹配
注意到这个模型和上述的二分图最大权完美匹配的差别为:它不要求是完美匹配。故而考虑删去所有负权边,再把不存在边的点对之间连上权值为
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?