匈牙利算法解决二分图最大匹配问题

题目如下:

给定一个二分图,其中左半部包含 n1 个点(编号 1∼n1 ),右半部包含 n2 个点(编号 1∼n2 ),二分图共包含 m 条边。 数据保证任意一条边的两个端点都不可能在同一部分中。 请你求出二分图的最大匹配数。 二分图的匹配:给定一个二分图 G ,在 G 的一个子图 M 中,M 的边集 {E} 中的任意两条边都不依附于同一个顶点,则称 M 是一个匹配。 二分图的最大匹配:所有匹配中包含边数最多的一组匹配被称为二分图的最大匹配,其边数即为最大匹配数。 输入格式 第一行包含三个整数 n1 、 n2 和 m 。 接下来 m 行,每行包含两个整数 u 和 v ,表示左半部点集中的点 u 和右半部点集中的点 v 之间存在一条边。 输出格式 输出一个整数,表示二分图的最大匹配数。

数据范围 :

1≤n1,n2≤500 ,

1≤u≤n1 , 1≤v≤n2 ,

1≤m≤10^5

思路:

  1. 初始化所有边的匹配状态为空,即 match[v] = 0,其中 v 是右半部的点。

  2. 从左半部的一个未匹配点开始,尝试找到一条增广路径。

  3. 在尝试找增广路径的过程中,使用深度优先搜索(DFS)来遍历与该左半部点相邻的右半部点,并尝试匹配它们。

  4. 如果找到一个未匹配的右半部点,或者已匹配的右半部点的匹配对象可以找到其他匹配(通过递归调用DFS),则将这两个点进行匹配。

  5. 继续尝试其他与当前左半部点相邻的右半部点,直到所有的右半部点都被访问过。

  6. 重复步骤2-5,不断寻找增广路径,直到无法找到增广路径为止,此时得到了一个最大匹配。

代码如下:

#include 
#include 

using namespace std;

const int MAXN = 505; // 最大点数,根据题目要求调整

vector edges[MAXN]; // 邻接表表示图
int match[MAXN]; // 记录右半部点的匹配情况
bool vis[MAXN]; // 记录左半部点是否已被访问

// 添加边
void addEdge(int u, int v) {
    edges[u].push_back(v);
}

// 尝试匹配左半部的某个点
bool dfs(int u) {
    for (int i = 0; i < edges[u].size(); ++i) {
        int v = edges[u][i];
        if (!vis[v]) {
            vis[v] = true;
            // 如果v没有匹配或者v的匹配对象可以找到其他匹配,那么就将u和v匹配
            if (match[v] == 0 || dfs(match[v])) {
                match[v] = u;
                return true;
            }
        }
    }
    return false;
}

// 匈牙利算法,返回最大匹配数
int hungarian(int n1) {
    int maxMatching = 0;
    for (int i = 1; i <= n1; ++i) {
        // 每次都需要清空左半部点的访问标记
        fill(vis, vis + n1 + 1, false);
        if (dfs(i)) {
            ++maxMatching;
        }
    }
    return maxMatching;
}

int main() {
    int n1, n2, m;
    cin >> n1 >> n2 >> m;

    for (int i = 0; i < m; ++i) {
        int u, v;
        cin >> u >> v;
        addEdge(u, v);
    }

    int maxMatching = hungarian(n1);
    cout << maxMatching << endl;

    return 0;
}

 

你可能感兴趣的:(算法,深度优先,图论,数据结构,c++)