# KM算法学习小记：

KM算法用于解决二分图最大权匹配问题，这个问题应该是可以用费用流就解决的。

https://blog.csdn.net/c20180630/article/details/71080521

https://www.cnblogs.com/huyufeifei/p/10350763.html

$$a[i][j]$$为左边第i个点到右边第j个点的最大边的权值，如果没有就是-inf。

http://uoj.ac/problem/80 这题并过不了。

Code(DFS):

#include<bits/stdc++.h>
#define fo(i, x, y) for(int i = x, _b = y; i <= _b; i ++)
#define ff(i, x, y) for(int i = x, _b = y; i <  _b; i ++)
#define fd(i, x, y) for(int i = x, _b = y; i >= _b; i --)
#define ll long long
#define pp printf
#define hh pp("\n")
using namespace std;

const int N = 405;

int nl, nr, m, x, y, z;
int a[N][N];

const int inf = 1e9;

int hl[N], hr[N], vl[N], vr[N], chox[N], choy[N];
int mi;

int find(int x) {
vl[x] = 1;
fo(y, 1, nr) {
if(vr[y]) continue;
int t = hl[x] + hr[y] - a[x][y];
if(!t) {
vr[y] = 1;
if(!choy[y] || find(choy[y])) {
chox[x] = y; choy[y] = x;
return 1;
}
} else mi = min(mi, t);
}
return 0;
}

int main() {
scanf("%d %d %d", &nl, &nr, &m);
fo(i, 1, m) {
scanf("%d %d %d", &x, &y, &z);
a[x][y] += z;
}
fo(i, 1, nl) fo(j, 1, nr) hl[i] = max(hl[i], a[i][j]);
int Nr = nr; nr = max(nr, nl);
fo(i, 1, nl) {
while(1) {
memset(vl, 0, sizeof vl);
memset(vr, 0, sizeof vr);
mi = inf;
if(find(i)) break;
fo(j, 1, nl) if(vl[j]) hl[j] -= mi;
fo(j, 1, nr) if(vr[j]) hr[j] += mi;
}
}
ll ans = 0;
fo(i, 1, nl) ans += hl[i];
fo(i ,1, nr) ans += hr[i];
pp("%lld\n", ans);
fo(i, 1, nl) pp("%d ", a[i][chox[i]] ? chox[i] : 0);
}

Code（BFS）：

#include<bits/stdc++.h>
#define fo(i, x, y) for(int i = x, _b = y; i <= _b; i ++)
#define ff(i, x, y) for(int i = x, _b = y; i <  _b; i ++)
#define fd(i, x, y) for(int i = x, _b = y; i >= _b; i --)
#define ll long long
#define pp printf
#define hh pp("\n")
using namespace std;

const int N = 405;

int nl, nr, m, x, y, z;
int a[N][N];

const int inf = 1e9;

int hl[N], hr[N], vl[N], vr[N], chox[N], choy[N], sla[N], pre[N];
int mi;

void bfs(int x) {
memset(sla, 127, sizeof sla);
memset(pre, 0, sizeof pre);
memset(vl, 0, sizeof vl);
memset(vr, 0, sizeof vr);
int u = 0, nu;
choy[u] = x;
do {
x = choy[u];
ll D = 1e9;
vr[u] = 1;
fo(y, 1, nr) if(!vr[y]) {
ll t = hl[x] + hr[y] - a[x][y];
if(t < sla[y]) {
sla[y] = t;
pre[y] = u;
}
if(sla[y] < D) {
D = sla[y], nu = y;
}
}
hl[choy[0]] -= D; hr[0] += D;
fo(i, 1, nr) {
if(vr[i]) {
hl[choy[i]] -= D, hr[i] += D;
} else sla[i] -= D;
}
u = nu;
} while(choy[u]);
for(; u; u = pre[u])
choy[u] = choy[pre[u]];
}

int main() {
scanf("%d %d %d", &nl, &nr, &m);
fo(i, 1, m) {
scanf("%d %d %d", &x, &y, &z);
a[x][y] += z;
}
fo(i, 1, nl) fo(j, 1, nr) hl[i] = max(hl[i], a[i][j]);
nr = max(nr, nl);
fo(i, 1, nl) bfs(i);
ll ans = 0;
fo(i, 1, nl) ans += hl[i];
fo(i, 1, nr) ans += hr[i];
fo(i, 1, nr) chox[choy[i]] = i;
pp("%lld\n", ans);
fo(i, 1, nl) pp("%d ", a[i][chox[i]] ? chox[i] : 0);
}

$$a[x][y]$$若没有边，则$$a[x][y]=0$$，且如果左边点比右边点多，则右边要补一些空点，这样当一个点匹配和它没有边的点时，相当于不选。

bfs的写法只能用邻接矩阵存边，就是两两点之间一定要有条边，不然UOJ那题的样例就会挂。

https://ac.nowcoder.com/acm/contest/4010/I

$$x[i]+y[j]>=a[i][j]>=0$$

$$\sum x+\sum y$$的最小值。

https://ac.nowcoder.com/acm/contest/view-submission?submissionId=42982840

KM算法学习小记：

(0)
(0)