本文分享的必刷题目是从蓝桥云课、洛谷、AcWing等知名刷题平台精心挑选而来,并结合各平台提供的算法标签和难度等级进行了系统分类。题目涵盖了从基础到进阶的多种算法和数据结构,旨在为不同阶段的编程学习者提供一条清晰、平稳的学习提升路径。
欢迎大家订阅我的专栏:算法题解:C++与Python实现!
附上汇总贴:算法竞赛备考冲刺必刷题(C++) | 汇总
【题目来源】
AcWing:861. 二分图的最大匹配 - AcWing题库
【题目描述】
给定一个二分图,其中左半部包含 n 1 n_1 n1 个点(编号 1 ∼ n 1 1\sim n_1 1∼n1),右半部包含 n 2 n_2 n2 个点(编号 1 ∼ n 2 1\sim n_2 1∼n2),二分图共包含 m m m 条边。
数据保证任意一条边的两个端点都不可能在同一部分中。
请你求出二分图的最大匹配数。
二分图的匹配:给定一个二分图 G G G,在 G G G 的一个子图 M M M 中, M M M 的边集 { E } \{E\} {E} 中的任意两条边都不依附于同一个顶点,则称 M M M 是一个匹配。
二分图的最大匹配:所有匹配中包含边数最多的一组匹配被称为二分图的最大匹配,其边数即为最大匹配数。
【输入】
第一行包含三个整数 n 1 n_1 n1、 n 2 n_2 n2 和 m m m。
接下来 m m m 行,每行包含两个整数 u u u 和 v v v,表示左半部点集中的点 u u u 和右半部点集中的点 v v v 之间存在一条边。
【输出】
输出一个整数,表示二分图的最大匹配数。
【输入样例】
2 2 4
1 1
1 2
2 1
2 2
【输出样例】
2
【算法标签】
《AcWing 861 二分图的最大匹配》 #匈牙利算法#
【代码详解】
#include <bits/stdc++.h> // 包含标准库头文件(万能头文件)
using namespace std; // 使用标准命名空间
const int N = 510; // 定义常量:最大顶点数
int n1, n2, m, ans; // 定义变量:左部顶点数、右部顶点数、边数、最大匹配数
vector<int> G[N]; // 定义邻接表:存储图的边关系
bool st[N]; // 定义数组:标记右部顶点是否被访问过
int match[N]; // 定义数组:记录右部顶点匹配的左部顶点
/**
* 寻找增广路径(匈牙利算法核心)
* @param x 当前尝试匹配的左部顶点
* @return 是否找到增广路径
*/
bool find(int x)
{
// 遍历当前顶点的所有邻接顶点
for (int i = 0; i < G[x].size(); i++)
{
int y = G[x][i]; // 获取右部顶点编号
if (st[y] == true) // 如果该顶点已被访问过
continue;
st[y] = true; // 标记为已访问
// 如果右部顶点未匹配,或者可以找到新的匹配
if (match[y] == 0 || find(match[y]))
{
match[y] = x; // 建立匹配关系
return true; // 返回找到增广路径
}
}
return false; // 未找到增广路径
}
int main()
{
cin >> n1 >> n2 >> m; // 输入左右顶点数和边数
// 构建图的邻接表
for (int i = 1; i <= m; i++)
{
int u, v;
cin >> u >> v; // 输入边的关系
G[u].push_back(v); // 添加边到邻接表
}
// 对每个左部顶点尝试寻找匹配
for (int i = 1; i <= n1; i++)
{
memset(st, false, sizeof(st)); // 重置访问标记
if (find(i)) // 如果找到增广路径
ans++; // 增加匹配数
}
cout << ans; // 输出最大匹配数
return 0; // 程序正常结束
}
//再写一遍
#include <bits/stdc++.h> // 包含标准库头文件(万能头文件)
using namespace std; // 使用标准命名空间
const int N = 510, M = 100010; // 定义常量:N为最大节点数,M为最大边数
vector<int> h[N]; // 定义邻接表:h[i]存储节点i的所有邻接节点
int n1, n2, m; // 定义变量:左半部节点数n1,右半部节点数n2,边数m
int match[N]; // 定义数组:match[i]表示右半部节点i匹配的左半部节点编号
bool st[N]; // 定义数组:st[i]标记右半部节点i在当前搜索中是否被访问过
/**
* 深度优先搜索寻找增广路径(匈牙利算法核心)
* @param u 当前尝试匹配的左半部节点
* @return 是否找到增广路径
*/
bool dfs(int u)
{
// 遍历当前节点的所有邻接节点
for (int i = 0; i < h[u].size(); i++)
{
int j = h[u][i]; // 获取右半部邻接节点编号
if (st[j] == 0) // 如果该节点未被访问过
{
st[j] = true; // 标记为已访问
// 如果该节点未匹配,或者可以找到新的匹配
if (match[j] == 0 || dfs(match[j]) == true)
{
match[j] = u; // 建立匹配关系
return true; // 返回找到增广路径
}
}
}
return false; // 未找到增广路径
}
int main()
{
cin >> n1 >> n2 >> m; // 输入左右节点数和边数
// 构建图的邻接表
while (m--)
{
int a, b;
cin >> a >> b; // 输入边的关系
h[a].push_back(b); // 添加边到邻接表(a为左半部,b为右半部)
}
int ans = 0; // 定义变量:存储最大匹配数
// 对每个左半部节点尝试寻找匹配
for (int i = 1; i <= n1; i++)
{
memset(st, false, sizeof(st)); // 重置访问标记
if (dfs(i) == true) // 如果找到增广路径
ans++; // 增加匹配数
}
cout << ans; // 输出最大匹配数
return 0; // 程序正常结束
}
【运行结果】
2 2 4
1 1
1 2
2 1
2 2
2