算法竞赛备考冲刺必刷题(C++) | AcWing 861 二分图的最大匹配

本文分享的必刷题目是从蓝桥云课洛谷AcWing等知名刷题平台精心挑选而来,并结合各平台提供的算法标签和难度等级进行了系统分类。题目涵盖了从基础到进阶的多种算法和数据结构,旨在为不同阶段的编程学习者提供一条清晰、平稳的学习提升路径。

欢迎大家订阅我的专栏:算法题解:C++与Python实现

附上汇总贴:算法竞赛备考冲刺必刷题(C++) | 汇总


【题目来源】

AcWing:861. 二分图的最大匹配 - AcWing题库

【题目描述】

给定一个二分图,其中左半部包含 n 1 n_1 n1 个点(编号 1 ∼ n 1 1\sim n_1 1n1),右半部包含 n 2 n_2 n2 个点(编号 1 ∼ n 2 1\sim n_2 1n2),二分图共包含 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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值