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

这是一个非常简单的问题,描述如下,给你两部分的点,问你最大匹配数是多少、
在这里插入图片描述

  • 简单描述一下算法流程,首先我们先匹配左边,蓝1先匹配红2;接下来到蓝2,只能匹配红2,但是这这时候发现红2已经有主了,根据匈牙利算法思想,这时候回溯,找谁匹配的红2,发现是蓝1,那么这时候看看蓝1还有没有别的选择,一看可以是红4,所以这时候让蓝1匹配红4,蓝2匹配红2;之后是蓝3,先让他匹配红1,没矛盾;现在得到的二分图如下所示
    在这里插入图片描述
  • 那么现在轮到蓝4了,他只能跟红4匹配,但是红4已经有主了,这时候回溯看蓝1,他还有一个选择是红2,但是红2也有主了,这时候回溯看蓝2,他没别的选择,所以全部返回 f a l s e false false,蓝4失配,所以最大匹配是3

上述过程实际上是在找一条从一个左边未匹配点到右边一个未匹配点的增广路径,上面并未介绍任何术语,因为二分图最大匹配问题是一个简单的问题,算法流程是很自然的

  • 因为算法最坏情况所构造的每个顶点的增广路可能会经过所有的边,所以算法时间复杂度是 O ( V E ) O(VE) O(VE)的,比较优秀

例题

https://www.luogu.com.cn/problem/P3386
模板

#include <bits/stdc++.h>

using namespace std;

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    int n, m, e;
    cin >> n >> m >> e;
    vector<vector<int> > g(n);
    while(e--){
        int u, v;
        cin >> u >> v;
        u -= 1;
        v -= 1;
        g[u].push_back(v);
    }
    vector<int> match(m, -1);
    int ans = 0;
    for(int i=0;i<n;i++){
        vector<int> vis(m);
        function<bool(int)> Dfs = [&](int u){
            for(auto i : g[u]){
                if(!vis[i]){
                    vis[i] = 1;
                    if(match[i] == -1 || Dfs(match[i])){
                        match[i] = u;
                        return true;
                    }
                }
            }
            return false;
        };
        if(Dfs(i)) ans += 1;
    }
    cout << ans;
    return 0;
}

http://acm.hdu.edu.cn/showproblem.php?pid=2063
也是模板

#include <bits/stdc++.h>

using namespace std;

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    int k, m, n;
    while(cin >> k){
        if(k == 0) break;
        cin >> m >> n;
        vector<vector<int> > g(m);
        for(int i=0;i<k;i++){
            int u, v;
            cin >> u >> v;
            u -= 1;
            v -= 1;
            g[u].push_back(v);
        }
        int ans = 0;
        vector<int> match(n, -1);
        for(int i=0;i<m;i++){
            vector<int> vis(n);
            function<bool(int)> Dfs = [&](int u){
                for(auto v : g[u]){
                    if(!vis[v]){
                        vis[v] = 1;
                        if(match[v] == -1 || Dfs(match[v])){
                            match[v] = u;
                            return true;
                        }
                    }
                }
                return false;
            };
            if(Dfs(i)) ans += 1;
        }
        cout << ans << '\n';
    }
    return 0;
}

https://www.luogu.com.cn/problem/P1129

  • 二分图匹配重要的在于建模能力,能不能看出来问题的本质。比如上面这个问题,它是一个二分图匹配的问题,为什么?
  • 问题描述很简单,一个 n × n n\times n n×n的矩阵,可以交换某两列或者某两行,问能不能让主对角线上元素都是1
  • 我们要进行一个思维转换,如果 a [ i ] [ j ] = 1 a[i][j]=1 a[i][j]=1,我们称 i , j i,j i,j构成一种对应关系,根据二分图思想,我们枚举第 i i i行,如果能找到最大匹配数是 n n n,那么是不是我们就可以进行某些移动来达到主对角线上元素都是1的目的了呢?因为二分图的两边各自都是 n n n个不同的数字
  • 进行这样的思维转换,能够发现这是一道匈牙利算法的裸题
#include <bits/stdc++.h>

using namespace std;

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    int t;
    cin >> t;
    while(t--){
        int n;
        cin >> n;
        vector<vector<int> > a(n, vector<int>(n));
        for(int i=0;i<n;i++){
            for(int j=0;j<n;j++){
                cin >> a[i][j];
            }
        }
        int ans = 0;
        vector<int> match(n, -1);
        for(int i=0;i<n;i++){
            vector<int> vis(n);
            function<bool(int)> Dfs = [&](int u){
                for(int i=0;i<n;i++){
                    if(!vis[i] && a[u][i] == 1){
                        vis[i] = 1;
                        if(match[i] == -1 || Dfs(match[i])){
                            match[i] = u;
                            return true;
                        }
                    }
                }
                return false;
            };
            if(Dfs(i)) ans += 1;
        }
        cout << (ans == n ? "Yes" : "No") << '\n';
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Clarence Liu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值