二分图的最大匹配

79 阅读2分钟

题目描述

给定一个二分图,其中左半部包含 n1n_1 个点(编号 1n11∼n_1),右半部包含 n2n_2 个点(编号 1n21∼n_2),二分图共包含 mm 条边。

数据保证任意一条边的两个端点都不可能在同一部分中。

请你求出二分图的最大匹配数。

二分图的匹配:给定一个二分图 GG,在 GG 的一个子图 MM 中,MM 的边集 {EE} 中的任意两条边都不依附于同一个顶点,则称 MM 是一个匹配。

二分图的最大匹配:所有匹配中包含边数最多的一组匹配被称为二分图的最大匹配,其边数即为最大匹配数。

输入格式

第一行包含三个整数 n1n_1、 n2n_2 和 mm

接下来 mm 行,每行包含两个整数 uu 和 vv,表示左半部点集中的点 uu 和右半部点集中的点 vv 之间存在一条边。

输出格式

输出一个整数,表示二分图的最大匹配数。

数据范围

1n1,n25001≤n_1,n_2≤500,
1un11≤u≤n_1,
1vn21≤v≤n_2,
1m1051≤m≤10^5

输入样例:

2 2 4
1 1
1 2
2 1
2 2

输出样例:

2

题目分析

这是一道关于匈牙利算法下的二分图匹配的问题。

首先我们按左右将节点按题意分为两部分,以左边节点为起始节点开始考虑,如果其所连边中的右侧节点有未曾被匹配的节点,则将这两个节点作为匹配的一对,并对答案产生 11 的贡献。

在上述思想下,我们来考虑一个问题,即如何选择每个左节点的最优右节点匹配。假设左节点 a,ba,b 都能与右节点 cc 进行匹配,且只有左节点 aa 能与右节点 dd 匹配,那么最终匹配对应为 ad,bca-d,b-c。若一开始我们将 a,ca,c 作为匹配的一对,在考虑 bb 的匹配时,便需要更改 aa 的匹配以形成最优结果。

那么我们如何更改 aa 的匹配呢,这时便需要从 aa 的剩余可连节点中找寻未被匹配的 dd,即用递归的方式进行查询。

在这里我们同时要引入 stst 数组,在我看来,这个数组的作用是为了减少递归的层数,避免内存爆栈。

由于每个节点最多会对所有边进行一次遍历,所以时间复杂度为 O(nm)O(nm)

Accept代码

#include <bits/stdc++.h>

using namespace std;

const int N = 510;
vector<int> g[N];
bool st[N];
int match[N];

bool find(int x)
{
    for (auto y : g[x])
        if (!st[y])
        {
            st[y] = true;
            if (!match[y] || find(match[y]))
            {
                match[y] = x;
                return true;
            }
        }
    return false;
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    
    int n1, n2, m;
    cin >> n1 >> n2 >> m;
    while (m --)
    {
        int a, b;
        cin >> a >> b;
        g[a].push_back(b);
    }
    int res = 0;
    for (int i = 1; i <= n1; i ++)
    {
        memset(st, 0, sizeof st);
        if (find(i)) res ++;
    }
    cout << res;
    return 0;
}