二分图普通匹配:匈牙利算法
//P3386 【模板】二分图最大匹配
#include<bits/stdc++.h>
using namespace std;
const int N = 510, M = 50010;
//
int tot = 0, ver[M], Head[N], Next[M];
void addEdge(int u, int v) {
ver[++tot] = v;
Next[tot] = Head[u], Head[u] = tot;
}
//
//如果点的下标从0开始,记得给match赋值为-1而不是0
int vis[N], match[N];
bool dfs(int u) {
for (int i = Head[u]; i; i = Next[i]) {
int v = ver[i];
if (!vis[v]) {
vis[v] = 1;
if (!match[v] || dfs(match[v])) {
match[v] = u;
return true;
}
}
}
return false;
}
//
int n, m, e;
int main()
{
scanf("%d%d%d", &n, &m, &e);
for (int i = 0; i < e; ++i) {
int u, v;
scanf("%d%d", &u, &v);
//只连一条有向边即可
addEdge(u, v);
}
int ans = 0;
memset(match, 0, sizeof(match));
for (int i = 1; i <= n; ++i) {
memset(vis, 0, sizeof(vis));
ans += dfs(i);
}
printf("%d", ans);
return 0;
}
匈牙利算法的时间戳优化
我们注意到,上面跑匈牙利的时候,每次都要 memset
一次,很烦,所以我们必须想办法进行优化。
我们不妨设置一个时间戳 now
,循环到第几个的时候就令时间戳为多少。在匈牙利内,改成这样:
//匈牙利部分
int now = 0;
int vis[N], match[N];
bool dfs(int u) {
for (int i = Head[u]; i; i = Next[i]) {
int v = ver[i];
if (vis[v] != now) {
vis[v] = now;
if (!match[v] || dfs(match[v])) {
match[v] = u;
return true;
}
}
}
return false;
}
//run
now = 0;
int ans = 0;
for (int i = 1; i <= n; ++i) {
now++;
if (!dfs(i)) ans++;
}
这样操作的正确性是显然的: vis[x]=now
就相当于以前的 vis[x]=1
,而 vis[x]!=now
就相当于 vis[x]=0
。
二分图最大带权匹配
可见《算法竞赛进阶指南》
KM算法在稠密图上效率较高,优化空间大,但是只能够处理完备匹配的情况。
费用流比较通用,但是有时候数据比较大的时候会慢,甚至超时。
二分图最小带权匹配
基于 BFS 的 KM 算法
普通 KM 算法的最坏复杂度为 ,数据过大的时候可能会超时,即使进行了其他诸如
slack优化
之类的东西也不一定能通过 。
使用 BFS 进行优化后(不用多次建立交错树),可以将复杂度压到。
#define LL long long
const int N = 310;
#define INF ((1LL) << 61)
int n;
LL dis[N][N];
//求最大匹配的情况下,不存在的边赋值为-INF
//如果是求最小匹配,那么所有边的边权取负,那么不存在的边就会赋值为INF
//KM
int vis[N], match[N], pre[N];
LL la[N], ra[N], slack[N];
void bfs(LL u) {
LL x, y = 0, yy = 0, delta;
memset(pre, 0, sizeof(pre));
for (int i = 1; i <= n; i++)
slack[i] = INF;
match[y] = u;
while (true) {
x = match[y], delta = INF, vis[y] = 1;
for (int i = 1; i <= n; i++) {
if (vis[i]) continue;
if (slack[i] > la[x] + ra[i] - dis[x][i]) {
slack[i] = la[x] + ra[i] - dis[x][i];
pre[i] = y;
}
if (slack[i] < delta)
delta = slack[i], yy = i;
}
for (int i = 0; i <= n; i++)
if (vis[i]) {
la[match[i]] -= delta;
ra[i] += delta;
}
else slack[i] -= delta;
y = yy;
if (match[y] == -1) break;
}
while (y) {
match[y] = match[pre[y]];
y = pre[y];
}
}
LL KM()
{
memset(match, -1, sizeof(match));
memset(la, 0, sizeof(la));
memset(ra, 0, sizeof(ra));
for (int i = 1; i <= n; i++) {
memset(vis, 0, sizeof(vis));
bfs(i);
}
LL ans = 0;
for (int i = 1; i <= n; i++)
ans += dis[match[i]][i];
return ans;
}