KM 算法
二分图的最大权完美匹配
不妨先看一些定义:
-
顶标
全称为“顶点标记值”。记左部点 $i$ 的顶标为 $lx_i$,右部点 $j$ 的顶标为 $ly_j$,那么要求顶标要满足 $lx_i+ly_j \ge w(i,j)$,其中 $w(i,j)$ 表示 $i$ 到 $j$ 那条边的边权。
-
相等子图
即原图中满足 $lx_i+ly_j = w(i,j)$ 的边构成的子图。
- 结论:若相等子图中存在完美匹配,则这个完美匹配就是二分图的带权最大完美匹配。
- 证明:完美匹配的边权和为 $(\sum_{i=1}^{n}lx_i)+(\sum_{i=1}^{n}ly_i)$。由于 $\forall i,j$,$lx_i+ly_j \ge w(i,j)$,故而整张二分图中,不存在有其他匹配的边权值和大于当前匹配的边权之和。
我们利用上述结论,成功的把问题转换为了:求一组合适的顶标,使得相等子图存在完美匹配。
有一种做法就是对于每一个 $i$,调整顶标使得有新边加入以达到能在相等子图里找到增广路、匹配的目的。若每一个 $i$ 都在上一个的基础上执行了这样的操作,便能得到一种完美匹配的方法。
-
不妨宏观匈牙利算法来明确一些性质:每次从未匹配边开始,以未匹配边、匹配边交错的形式形成一颗交错树,其中左部节点沿非匹配边访问到右部节点,右部节点沿匹配边找到原来匹配的左部节点。且未找到增广路之前,不会改变已有的匹配。
假设目前的相等子图里找不到增广路了,我们需要一种调整顶标的方法,使得原图中存在的边现在有,且至少一条原来没有的边新图有了。
-
调整方法:我们还是设左部节点为 $i$,右部节点为 $j$。之后把所有的所有访问到过的 $lx_i$ 加上 $v$、$ly_j$ 减去 $v$。
进一步转化为如何求一个合适的 $v$,使得满足上述条件。
因为左部节点的访问是被动的(即被右部节点沿匹配边访问到),且考虑 $i \notin \text{交错树},j \notin \text{交错树}$ 没有意义($\notin \text{交错树}$ 的顶标都没有变化),所以我们只用考虑 $i \in \text{交错树}$,且只用考虑有连边的 $i,j$。
分情况来讨论:
- $j \in \text{交错树}$。
那么连接两点的这条边显然是匹配边,那么两边一个加 $v$,一个减 $v$,显然 $a_i+b_j=w(i,j)$,依旧是新相等子图中的边。
- $j \notin \text{交错树}$。
在所有访问过的 $lx$ 加 $v$,$ly$ 减 $v$ 之后满足顶标的性质——$lx_i+ly_j \ge w(i,j)$,当且仅当 $v \le lx_i+ly_j-w(i,j)$。若在所有的 $lx_i+ly_j-w(i,j)$ 中取最小的值作为 $v$ 的值,那么就能达成“既满足顶标的性质,又能至少有一条边加入新相等子图”。
具体的,有以下步骤:
-
枚举点 $i$
思想在于不断通过调整顶标加入边以使得 $i$ 能找到匹配。
故重复以下过程直至 $i$ 找到匹配。
-
进行寻找增广路同时获得合适的 $v$。
时间复杂度 $O(n+m)$。
-
调整顶标。
时间复杂度 $O(n)$。
-
时间复杂度 $O(n(n+m))=O(n^2+nm)$。
#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;
}
二分图的最大权匹配
注意到这个模型和上述的二分图最大权完美匹配的差别为:它不要求是完美匹配。故而考虑删去所有负权边,再把不存在边的点对之间连上权值为 $0$ 的边。最后跑 $\text{KM}$ 即可。

浙公网安备 33010602011771号