54.Acwing基础课第861题-简单-二分图的最大匹配
54.Acwing基础课第861题-简单-二分图的最大匹配
题目描述
\(给定一个二分图,其中左半部包含 n_1 个点(编号 1∼n_1),右半部包含 n_2 个点(编号 1∼n_2),二分图共包含 m 条边。\)
\(数据保证任意一条边的两个端点都不可能在同一部分中。\)
\(请你求出二分图的最大匹配数。\)
\(二分图的匹配:给定一个二分图 G,在 G 的一个子图 M 中,M 的边集 {E} 中的任意两条边都不依附于同一个顶点,则称 M 是一个匹配\)。
\(二分图的最大匹配:所有匹配中包含边数最多的一组匹配被称为二分图的最大匹配,其边数即为最大匹配数\)。
输入格式
\(第一行包含三个整数 n1、 n2 和 m\)。
\(接下来 m 行,每行包含两个整数 u 和 v,表示左半部点集中的点 u 和右半部点集中的点 v 之间存在一条边\)。
输出格式
输出一个整数,表示二分图的最大匹配数。
数据范围
\(1≤n_1,n_2≤500\),
\(1≤u≤n_1\),
\(1≤v≤n_2\),
\(1≤m≤10^5\)
输入样例:
2 2 4
1 1
1 2
2 1
2 2
输出样例:
2
代码:
// 包含memset函数所需头文件(用于初始化邻接表和访问标记数组)
#include <cstring>
// 标准输入输出头文件(scanf/printf依赖)
#include <iostream>
// 算法库(本代码未直接使用,属于通用模板保留)
#include <algorithm>
// 使用std命名空间,避免重复写std::
using namespace std;
// 常量定义:
// N:二分图左右集合的最大顶点数(题目通常约束510级别)
// M:最大边数(适配稀疏图场景)
const int N = 510, M = 100010;
// 全局变量:
int n1, n2, m; // n1:左集合顶点数,n2:右集合顶点数,m:边数
// 邻接表存储二分图(仅存左集合→右集合的边,无需双向):
int h[N]; // h[u]:左集合顶点u的邻接表表头(初始为-1)
int e[M]; // e[idx]:第idx条边的终点(右集合顶点)
int ne[M]; // ne[idx]:第idx条边的下一条边索引(链表结构)
int idx; // 边计数器(新增边时自增)
int match[N]; // 匹配数组:match[j]表示右集合顶点j当前匹配的左集合顶点(0表示未匹配)
bool st[N]; // 访问标记数组:标记右集合顶点j是否被「当前左顶点」访问过(避免重复递归)
// 邻接表添加边函数(仅添加左→右的边,二分图匹配方向固定)
// a:左集合顶点,b:右集合顶点
void add(int a, int b)
{
e[idx] = b; // 记录第idx条边的终点是右顶点b
ne[idx] = h[a]; // 让第idx条边指向左顶点a当前的表头(链表前驱)
h[a] = idx ++ ; // 更新a的表头为当前边索引,边计数器+1
}
// 核心递归函数:查找左集合顶点x的匹配(找增广路)
// 返回值:true=找到匹配,false=未找到匹配
// 增广路思想:从x出发,找到一条“未匹配边→匹配边→未匹配边”的路径,扩展匹配数
bool find(int x)
{
// 遍历左顶点x的所有邻接右顶点(邻接表遍历)
for (int i = h[x]; i != -1; i = ne[i])
{
int j = e[i]; // j是x的邻接右顶点
if (!st[j]) // 若右顶点j未被当前左顶点x访问过(避免重复递归导致死循环)
{
st[j] = true; // 标记j已被访问,后续同一轮递归不再处理
// 匹配成功的两种情况:
// 1. 右顶点j未匹配(match[j]==0),直接将x与j匹配;
// 2. j已匹配,但j的匹配左顶点match[j]能找到其他匹配(递归找增广路),则j“让”给x
if (match[j] == 0 || find(match[j]))
{
match[j] = x; // 将右顶点j匹配给左顶点x
return true; // 匹配成功,返回true
}
}
}
return false; // x的所有邻接右顶点都无法匹配,返回false
}
int main()
{
// 输入:左集合顶点数n1,右集合顶点数n2,边数m
scanf("%d%d%d", &n1, &n2, &m);
// 初始化邻接表表头:所有左顶点的表头设为-1(初始无邻边)
memset(h, -1, sizeof h);
// 输入m条边,构建左→右的邻接表
while (m -- )
{
int a, b;
scanf("%d%d", &a, &b); // a∈左集合,b∈右集合
add(a, b); // 仅添加左→右的边,无需反向
}
int res = 0; // res:二分图最大匹配数
// 遍历左集合的每一个顶点,尝试为其找匹配
for (int i = 1; i <= n1; i ++ )
{
// 关键:每次处理新的左顶点i时,清空访问标记数组st
// 原因:st仅约束“同一左顶点”的递归过程,不同左顶点的访问标记互不影响
memset(st, false, sizeof st);
if (find(i)) res ++ ; // 若i找到匹配,匹配数+1
}
// 输出二分图最大匹配数
printf("%d\n", res);
return 0;
}

浙公网安备 33010602011771号