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