Loading

二分图匹配

二分图的概念

二分图又称作二部图,是图论中的一种特殊模型。设\(G=(V,E)\)是一个无向图,如果顶点\(V\)可分割为两个互不相交的子集\((A,B)\),并且图中的每条边\((i,j)\)所关联的两个顶点i和j分别属于这两个不同的顶点集\((i \in A,j \in B)\),则称图\(G\)为一个二分图。
二分图
上图是一个标准的二分图。

最大匹配

最大匹配即是选择其中边数最大的子集的图。
二分图匹配主要有两种算法:匈牙利算法和使用dinic的二分图匹配

匈牙利算法

建立有向图\(G\),分为二分图的左侧和右侧。
优先选择左侧序号更小的连接可能的边。
对于两个点的目标点“冲突”的时候,采取“协商”的办法。
即序号小的连接可能连接的另一条边。
若“协商”失败,则放弃序号较大的点的边。
一个寻找增广路的过程
复杂度\(o(n*m)\) \(n\)为点数,\(m\)为边数

使用Dinic的二分图匹配

建立超级源点和超级汇点,使得源点与所有左侧点连接并权值为1,汇点与所有右侧点连接并权值为1,计算从超级源点到超级汇点的最大流就是最大匹配
复杂度\(o(n\sqrt{m})\)

模版

洛谷模版

匈牙利算法版

#include <bits/stdc++.h>
using namespace std;
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#define de(a) cout << #a << " = " << a << endl
#define rep(i, a, n) for (int i = a; i <= n; i++)
#define per(i, a, n) for (int i = n; i >= a; i--)
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef vector<int, int> VII;
#define inf 0x3f3f3f3f
const ll INF = 0x3f3f3f3f3f3f3f3f;
const ll MAXN = 2e3 + 7;
const ll MAXM = 2e6 + 7;
const ll MOD = 1e9 + 7;
const double eps = 1e-6;
const double pi = acos(-1.0);
struct edge
{
    int to, nxt;
} e[MAXM];
int n, m, num, cnt = -1, head[MAXN], match[MAXN];
bool f[MAXN];
void add(int from, int to)
{
    e[++cnt].to = to;
    e[cnt].nxt = head[from];
    head[from] = cnt;
}
bool hungarian(int u)
{
    for (int i = head[u]; i != -1; i = e[i].nxt)
    {
        int v = e[i].to;
        if (!f[v])
        {
            f[v] = 1;
            if (!match[v] || hungarian(match[v]))
            {
                match[v] = u;
                return 1;
            }
        }
    }
    return 0;
}
void init()
{
    memset(head, -1, sizeof(head));
    cnt = -1;
}
int main()
{
    /*    freopen("k.in", "r", stdin);
    freopen("k.out", "w", stdout); */
    init();
    cin >> n >> m >> num;
    while (num--)
    {
        int u, v;
        cin >> u >> v;
        if (u > n || v > m)
            continue;
        add(u, v);
    }
    int ans = 0;
    for (int i = 1; i <= n; i++)
    {
        memset(f, 0, sizeof(f));
        if (hungarian(i))
            ans++;
    }
    cout << ans << endl;
    return 0;
}

Dinic版

#include <bits/stdc++.h>
using namespace std;
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#define de(a) cout << #a << " = " << a << endl
#define rep(i, a, n) for (int i = a; i <= n; i++)
#define per(i, a, n) for (int i = n; i >= a; i--)
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef vector<int, int> VII;
#define inf 0x3f3f3f3f
const ll INF = 0x3f3f3f3f3f3f3f3f;
const ll MAXN = 2e3 + 7;
const ll MAXM = 2e6 + 7;
const ll MOD = 1e9 + 7;
const double eps = 1e-6;
const double pi = acos(-1.0);
int cnt = -1, head[MAXM], dis[MAXN], cur[MAXM];
struct edge
{
    int to, value, net;
} e[MAXM << 1]; ///共有n*2条边
void add(int from, int to, int value)
{ ///链式前向星
    cnt++;
    e[cnt].to = to;
    e[cnt].value = value;
    e[cnt].net = head[from];
    head[from] = cnt;
}
int bfs(int st, int ed)
{ ///建立层次图
    queue<int> que;
    memset(dis, -1, sizeof(dis));
    dis[st] = 0;
    que.push(st);
    while (!que.empty())
    {
        int x = que.front();
        que.pop();
        for (int i = head[x]; i > -1; i = e[i].net)
        {
            int now = e[i].to;
            if (dis[now] == -1 && e[i].value)
            {
                que.push(now);
                dis[now] = dis[x] + 1;
            }
        }
    }
    return dis[ed] != -1;
}
int dfs(int x, int t, int maxflow)
{
    if (x == t)
        return maxflow;
    int ans = 0;
    for (int i = cur[x]; i > -1; i = e[i].net)
    { ///当前弧优化
        int now = e[i].to;
        if (dis[now] != dis[x] + 1 || e[i].value == 0 || ans >= maxflow)
            continue;
        cur[x] = i;
        int f = dfs(now, t, min(e[i].value, maxflow - ans));
        e[i].value -= f;
        e[i ^ 1].value += f; ///反向边加流量
        ans += f;
    }
    if (!ans)
        dis[x] = -1; ///炸点优化
    return ans;
}
int Dinic(int st, int ed)
{
    int ans = 0;
    while (bfs(st, ed))
    {
        memcpy(cur, head, sizeof(head));
        int k;
        while ((k = dfs(st, ed, inf)))
            ans += k;
    }
    return ans;
}
void init()
{
    cnt = -1;
    memset(head, -1, sizeof(head));
}
int main()
{
    init();
    int n, m, ee;
 /*    freopen("k.in", "r", stdin);
    freopen("k.out", "w", stdout); */
    scanf("%d%d%d", &n, &m, &ee);
    int s = n + m + 1, t = n + m + 2; //超级源点 超级汇点
    for (int i = 1; i <= n; i++)
    {
        add(s, i, 1);
        add(i, s, 0);
    }
    for (int i = 1; i <= m; i++)
    {
        add(i + n, t, 1);
        add(t, i + n, 0);
    }
    while (ee--)
    {
        int u, v, t;
        scanf("%d%d", &u, &v);
        /* 数据有坑 */
        if (v > m || u > n)
            continue;
        v += n;
        add(u, v, 1);
        add(v, u, 0);
    }
    printf("%d\n", Dinic(s, t));
    return 0;
}
posted @ 2019-08-21 10:35  GrayKido  阅读(257)  评论(0编辑  收藏  举报