【匹配】匈牙利匹配算法

every blog every motto: You can do more than you think.
https://blog.csdn.net/weixin_39190382?type=blog

0. 前言

匈牙利匹配算法

1. 正文

1.1 基础概念

二分图

顶点分为两个集合,集合间顶点相连,集合内点不相连

请添加图片描述

匹配

一个匹配就是一个边的集合,其中,任意两条边不存在公共的顶点

最大匹配

上图中,我们能找到多组匹配,在这其中,所含匹配变数最多的的匹配,成为最大匹配。

交替路

从一个未匹配的点出发,依次经过非匹配边,匹配边,非匹配边,这样形成的路称为交替路

增广路

从一个未匹配的点出发,走交替路,如果途径另一个未匹配点(起点不算),则这条交替路称为增广路

1.2 算法流程

1.2.1 通俗理解

概括: 先到先得,能让则让

对于如下已有的匹配,我们需要寻找他的最大匹配。
请添加图片描述

1.2.2 步骤

下面以经典的配对情景进行说明。

现在有boys和girls两个点集,每个人有各自的暧昧对象(可能有多个),现在要把他们配情侣,最多能配多少对?(即,寻找最大匹配数)

说明:

  1. 边表示暧昧关系

注意:

  1. 不能搞基哦
  2. 不能脚踏多只船

请添加图片描述
先从B1看起,他与G2有暧昧,那么先暂时他们配成一对。
注意: 这里突出了先到先得

请添加图片描述
再看B2,B2也可G2纠缠不清,那么我们看G2现在有男友吗?有的,是B1。那B1有没有其他暧昧对象呢?花心男果然是花心男,还有一个G4,同时,G4还没有安排对象。那么把B1和G4配成一对。

注意: 这里突出了能让则让
请添加图片描述
再接着,我们看B3,他这G1直接配对就好。

最后我们看B4,他的暧昧对象是G4,但是G4现在已经有男友了。按照之前的思路,往前到,看看能不能让一让的。发现没法让,所以B4只能单着了。
同时,G3也只能单着。

注意: 这里依然是能让则让,让不了也没法

请添加图片描述
至此,找到一个了最大匹配,但是最大匹配不是固定的。如果从girls这边开始或者从B4开始情况可能就不一样了。但是最大匹配数是一样的。

1.2.3 代码实现

int M,N; // M,N 分表表示左右则集合的元素个数
int Map[MAXM][MAXN]; // 邻接矩阵存图
int p[MAXN]; // 记录当前右侧元素所对应的左侧元素
bool vis[MAXN]; // 记录右侧元素是否已被访问过

bool match(int i){
    for(int j=1;j<=N;++j){ 
        // 有边且未访问
        if(Map[i][j]&&!vis[j]){ 
            vis[j] = true; // 记录访问过
            // 无匹配 或者 原匹配左侧元素可以找到新的匹配
            if(p[j]==0 || match(p[j])){ 
                p[j]=i; // 当前左侧元素成为当前右侧元素的新匹配
                return true // 返回匹配成功
            }
        }
    }
    return false;
}

int Hungarian(){
    int cnt =0;
    for(int i=1; i<=N; ++i){
        memset(vis,false,sizeof(vis)); // 重置vis数组
        if(match(i)) cnt++;
    }
    return cnt
}

1.3 最小点覆盖问题

另外一个关于二分图的问题是求最小点覆盖:我们想找到最少的一些点,使二分图所有的边都至少有一个端点在这些点之中。倒过来说就是,删除包含这些点的边,可以删掉所有边。

20240424115504

(König定理):

一个二分图中的最大匹配数等于这个图中的最小点覆盖数。

1.4 应用

1.4.1 案例一

题目:
面对OIBH组织的嚣张气焰, 柯南决定深入牛棚, 一探虚实.
他经过深思熟虑, 决定从OIBH组织大门进入…
OIBH组织的大门有一个很神奇的锁.
锁是由M*N个格子组成, 其中某些格子凸起(灰色的格子). 每一次操作可以把某一行或某一列的格子给按下去.

20240424115246

如果柯南能在组织限定的次数内将所有格子都按下去, 那么他就能够进入总部. 但是OIBH组织不是吃素的, 他们的限定次数恰是最少次数.
请您帮助柯南计算出开给定的锁所需的最少次数.

读入/Input:

第一行 两个不超过100的正整数N, M表示矩阵的长和宽
以下N行 每行M个数 非0即1 1为凸起方格

输出/Output:

一个整数 所需最少次数


如果我们把样例的矩阵,转化为二分图的形式,是这样的:

20240424115336

按下一行或一列,其实就是删掉与某个点相连的所有边。现在要求最少的操作次数,想想看,这不就是求最小点覆盖数吗?所以直接套匈牙利算法即可。代码略。

参考

  1. https://zhuanlan.zhihu.com/p/96229700
  2. https://zhuanlan.zhihu.com/p/62981901
  • 23
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

胡侃有料

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

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

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

打赏作者

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

抵扣说明:

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

余额充值