题目描述
- 给定一个二分图,其中左半部包含
n1
个点(编号1∼n1
),右半部包含n2
个点(编号1∼n2
),二分图共包含m
条边。 - 数据保证任意一条边的两个端点都不可能在同一部分中。
- 请你求出二分图的最大匹配数。
- 二分图的匹配:给定一个二分图
G
,在G
的一个子图M
中,M
的边集{E}
中的任意两条边都不依附于同一个顶点,则称M
是一个匹配。 - 二分图的最大匹配:所有匹配中包含边数最多的一组匹配被称为二分图的最大匹配,其边数即为最大匹配数。
输入格式
- 第一行包含三个整数
n1
、n2
和m
。 - 接下来
m
行,每行包含两个整数u
和v
,表示左半部点集中的点u
和右半部点集中的点v
之间存在一条边。
输出格式
- 输出一个整数,表示二分图的最大匹配数。
基本思路
首先,我们需要理解二分图中的匹配是什么。
这个问题可以想象成是在帮一群男生和一群女生进行最佳配对。二分图中的一部分点代表男生,另外一部分点代表女生,而图中的边表示某个男生和某个女生相互之间的暧昧关系。匹配就是指任何一个男生只能和与其暧昧的一个女生配对成为情侣或者单身,而最大匹配问题就是求出这些男生女生中最多产生多少对情侣。
本题是计算机图论中匈牙利算法的模板题,下面给出详细的算法分析:
- 算法功能:在一个二分图中查找出最大匹配的数量。
- 算法流程:
- 初步配对:首先,让每个男生尝试去邀请他们喜欢的女生之一。如果某个女生同时被多个男生邀请,她只能接受其中第一个男生的邀请。
- 寻找未配对者:现在,检查一下哪些男生还没有找到配对。这些男生将开始寻找新的机会。
- 调整配对:对于每个未配对的男生,看看他们喜欢的女生是否已经有了配对。如果是,那么这个男生会尝试让那个女生放弃现有的配对,转而和他配对。为了做到这一点,男生会去查看这个女生是否有其他喜欢的男生(也就是其他可选的配对),如果有,那么原来的配对就有可能被调整。
- 递归调整:如果一个女生因为新的邀请而放弃了原来的配对,那么原来的男生又变成了未配对者,他也会尝试寻找新的配对。这个过程会一直持续下去,直到所有未配对的男生都尝试过或者所有的女生都找到了配对。
- 重复步骤:如果在某个时刻,所有的男生都尝试过了,但是仍然有男生未配对,那么回到步骤2,继续寻找新的配对机会。这个过程会一直重复,直到不能再找到新的配对为止。
实现代码(C++)
#include <cstdio>
#include <vector>
using namespace std;
const int N = 505;
int n1, n2, m;
int u, v;
vector<int> edges[N];
int max_match_count;
int matched[N];
bool visited[N];
bool find_another_right(int left)
{
for (int right : edges[left])
{
if (!visited[right])
{
visited[right] = true;
if (matched[right] == 0 || find_another_right(matched[right]))
{
matched[right] = left;
return true;
}
}
}
return false;
}
int get_max_match_count(void)
{
int result = 0;
for (int left = 1; left <= n1; ++left)
{
fill(visited, visited + N, false);
if (find_another_right(left))++result;
}
return result;
}
int main(void)
{
scanf("%d%d%d", &n1, &n2, &m);
for (int i = 0; i < m; ++i)
{
scanf("%d%d", &u, &v);
edges[u].push_back(v);
}
max_match_count = get_max_match_count();
printf("%d", max_match_count);
return 0;
}