题解:AcWing 861 二分图的最大匹配

【题目来源】

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

【题目描述】

给定一个二分图,其中左半部包含 \(n_1\) 个点(编号 \(1\sim n_1\)),右半部包含 \(n_2\) 个点(编号 \(1\sim n_2\)),二分图共包含 \(m\) 条边。

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

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

二分图的匹配:给定一个二分图 \(G\),在 \(G\) 的一个子图 \(M\) 中,\(M\) 的边集 \(\{E\}\) 中的任意两条边都不依附于同一个顶点,则称 \(M\) 是一个匹配。

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

【输入】

第一行包含三个整数 \(n_1\)\(n_2\)\(m\)

接下来 \(m\) 行,每行包含两个整数 \(u\)\(v\),表示左半部点集中的点 \(u\) 和右半部点集中的点 \(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
posted @ 2026-02-21 20:51  团爸讲算法  阅读(6)  评论(0)    收藏  举报