二分图普通匹配:匈牙利算法
//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而不是0int 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;
}
