首页 > 编程语言 >二分图最大匹配-匈牙利算法

二分图最大匹配-匈牙利算法

时间:2024-10-09 23:47:30浏览次数:3  
标签:二分 匹配 增广 int 匈牙利 vis 算法 节点 match

二分图最大匹配

设 G为二分图,若在 G 的子图 M中,任意两条边都没有公共节点,那么称M为二分图G的一组匹配。在二分图中,包含边数最多的一组匹配称为二分图的最大匹配。
在这里插入图片描述

交替路:从一个未匹配点出发,依次经过非匹配边、匹配边、非匹配边…形成的路径叫交替路。

增广路:从一个未匹配点出发,走交替路,若能到达另一个未匹配点,则这条交替路称为增广路。
例如,3→5→1→4→2→7
如下图所示:
在这里插入图片描述

观察增广路,我们会发现:非匹配边比匹配边多一条。只要把增广路中的匹配边和非匹配边的身份交换(即倒过来走),交换后,图中的匹配边数目比原来多了1条。
如图所示:
在这里插入图片描述

这里的增广路就是指能增加匹配边的一条路。
匈牙利算法:通过不停地找增广路来增加匹配边。找不到增广路时,达到最大匹配。可以用DFS或BFS来实现。

匈牙利算法

男女相亲,男选女,可占可让,贪心配对

在这里插入图片描述
我们来看一道例题:
洛谷P3386

代码如下:

#include<iostream> // 引入输入输出库
using namespace std;

// 声明全局变量
int n, m, k, a, b, ans; // n: 左边节点数, m: 右边节点数, k: 边数, a, b: 边的两个端点, ans: 匹配的总数
const int M = 500005; // 定义最大边数
const int N = 500005; // 定义最大节点数
int h[N], idx; // h: 邻接表头, idx: 当前边的索引
int vis[N], match[N]; // vis: 访问标记数组, match: 匹配数组

// 边的结构体定义
struct edge {
    int v; // 边的终点
    int ne; // 下一个边的索引
} e[M]; // 声明边的数组

// 添加边到邻接表
void add(int a, int b) {
    e[++idx] = { b,h[a] }; // 将边(b, h[a])加入数组e,并更新邻接表
    h[a] = idx; // 更新节点a的邻接表头指向当前边
}

// 深度优先搜索尝试寻找增广路径
bool dis(int u) {
    // 遍历以u为起点的所有边
    for (int i = h[u]; i; i = e[i].ne) {
        int v = e[i].v; // 获取边的终点v
        if (vis[v]) { // 如果v已经被访问过
            continue; // 跳过
        }
        vis[v] = 1; // 标记v为已访问
        // 如果v没有匹配或者找到v的匹配节点的增广路径
        if (!match[v] || dis(match[v])) {
            match[v] = u; // 更新v的匹配为u
            return 1; // 找到一条增广路径
        }
    }
    return 0; // 未找到增广路径
}

int main() {
    cin >> n >> m >> k; // 输入左边节点数n、右边节点数m和边数k
    for (int i = 0; i < k; i++) { // 遍历每一条边
        cin >> a >> b; // 输入边的两个端点a和b
        add(a, b); // 添加边到邻接表
    }

    for (int i = 1; i <= n; i++) { // 对于每一个左边节点
        memset(vis, 0, sizeof(vis)); // 重置访问标记数组
        if (dis(i)) { // 尝试从节点i出发寻找增广路径
            ans++; // 如果找到增广路径,匹配数增加
        }
    }

    cout << ans << endl; // 输出最终的匹配数
    return 0; // 返回0,程序正常结束
}

标签:二分,匹配,增广,int,匈牙利,vis,算法,节点,match
From: https://blog.csdn.net/buaichifanqie/article/details/142789782

相关文章

  • 算法修炼之路之前缀和
    目录一:一维前缀和二:二维前缀和 三:LeetCodeOJ练习  1.第一题2.第二题 3.第三题 4.第四题5.第五题6.第六题一:一维前缀和这里通过例题来引出牛客_DP34【模板】前缀和画图分析:具体代码:#include<iostream>#include<vector>usingnamespacestd;int......
  • 算法校赛准备
    独木桥题目背景战争已经进入到紧要时间。你是运输小队长,正在率领运输部队向前线运送物资。运输任务像做题一样的无聊。你希望找些刺激,于是命令你的士兵们到前方的一座独木桥上欣赏风景,而你留在桥下欣赏士兵们。士兵们十分愤怒,因为这座独木桥十分狭窄,只能容纳$1$个人通过。假如......
  • 代码随想录算法训练营 | 背包问题 二维,背包问题 一维,416. 分割等和子集
    背包问题二维题目链接:背包问题二维文档讲解︰代码随想录(programmercarl.com)视频讲解︰背包问题二维日期:2024-10-09想法:dp[i][j],i表示需要从物品0-i中选择加入到背包中,j表示背包的容量,dp值表示最大的价值;递推公式,如果背包大小j都比此时要放的物品i的weight[i]小了,背包放不下......
  • 代码随想录算法训练营day10| 232.用栈实现队列 225. 用队列实现栈 20. 有效的括
    学习资料:https://programmercarl.com/栈与队列理论基础.html栈与队列学习记录:232.用栈实现队列(两个栈(stack_in,stack_out)实现一个队列的行为)点击查看代码classMyQueue(object):def__init__(self):self.stack_in=[]self.stack_out=[]d......
  • codeforces round 974(div.3)E(优先队列实现dijstra算法,devc++的优先队列用greater报
    解题历程:看到两边同时移动,计算最终的相遇时间,我就想到两边同时计算各点到起点的最短距离,就是使用dijstra算法,最后所有节点取两次计算的最大值,再对所有节点取最小值,就是最终答案了,可是这个思路没有考虑有马的情况,思考一番后发现可以多列一个数组记录有马的情况下的行走最短路,然后......
  • 必知的十大计算机视觉算法
    成长路上不孤单......
  • JAVA——常见算法
    查找算法基本查找从0索引开始查找是否找到packagecom.itheima.search;importjava.security.KeyStore;publicclassBasicSearchDemo1{publicstaticvoidmain(String[]args){int[]arr={23,34,54,24,43,46};intnumber=43;......
  • 简明逻辑回归算法
     逻辑回归是一种用于分类问题的统计方法,尽管名称中包含“回归”,但它主要用于二分类任务。为了更好地理解逻辑回归,我们可以通过一个通俗易懂的例子来解释。例子:判断是否通过考试假设你是一名老师,想要根据学生的学习时间来判断他们是否能通过一次考试。我们将“通过考试”定义为......
  • DAY27||回溯算法基础 | 77.组合| 216.组合总和Ⅲ | 17.电话号码的字母组合
    回溯算法基础知识一种效率不高的暴力搜索法。本质是穷举。有些问题能穷举出来就不错了。回溯算法解决的问题有:组合问题:N个数里面按一定规则找出k个数的集合切割问题:一个字符串按一定规则有几种切割方式子集问题:一个N个数的集合里有多少符合条件的子集排列问题:N个数按一定规......
  • 不同的快排算法
    之前写过一篇快排但是现在来看写的很简单,很无聊 快排的思想其实大家都懂这次详细写写不同快排之间的区别和一些优化点 1.首先是pivot元素的选择a.当我们数组本身就是随机的时候,选择第一个/最后一个/中间一个都是可以的,但如果数组是有某种规律的,有可能会退化......