【洛谷P3386】二分图最大匹配之Kuhn算法/匈牙利算法:直观理解

news2025/5/19 20:07:46

题目:洛谷P3386 【模板】二分图最大匹配

🥕 匈牙利算法本来是针对带权图最大匹配的,这里由于题目只是求最大匹配的边数,所以我们也只考虑无权的情况。

🚀 本文旨在服务于看了别的关于匈牙利算法的文章但不甚理解的童鞋,希望能够从直观上帮大家看清算法的巧妙之处,而关于匈牙利算法的历史渊源、主要流程等则不再赘述。


关于二分图最大匹配我们可以有如下直观的理解:我们假设有一群人和一堆物品,我们知道每个人喜欢这些物品中的哪几个,每个人可以分得一个物品,求最多可以使多少人分到自己喜欢的某个物品。我们令人的编号是ABCDEF……,物品的编号是123456……。

⭐️ 那好啊,我们先看人A。他喜欢哪个物品,我们就先给他呗,随便选一个他喜欢的就行(当然如果他什么都不喜欢就只能晾在一边了)。设分给A的物品为x。
⭐️ 再看人B。如果他喜欢一个没有分配给A的物品y,那太好了,把y分配给B即可。但如果B只喜欢已经分配给A的那个物品x,B就只能空手而归了吗?也不尽然。我们可以把x从A手里抢过来给B,然后看给A能不能分配一个别的。如果A除了喜欢x以外还喜欢一个物品z,那就好办了,把z给A,把x给B,皆大欢喜。但如果A也喜欢物品x,那没办法,A和B势必只能满足一个,没法在把x给B的情况下给A补偿一个别的,所以只能委屈一下B了。

……

⭐️ 不管考虑哪个人(设为P),我们总是可以遵循以下套路:如果P喜欢的物品中有未被认领的,那么直接分配给P就好了;否则就依次检查P喜欢的物品,如果可以想办法让这个物品的持有者Q另觅他物,那就把这个物品抢过来;如果他抢夺所有喜欢的物品都失败了(即物品持有者都没法腾出来),那他就分不到物品了。此外,在令当前物品持有者Q更换物品的时候,也需要进行相同的流程:如果Q喜欢的物品当中还有其他未被占领的,就分配给Q,否则也是看他喜欢的物品除了给P的之外有没有可能让持有者腾出来。那么,这就形成了一个递归结构,如果某一层的人获得了未被占领的物品,那么就P就被分配到了物品;如果所有辗转腾挪的方法都无效,那么P就不会获得物品。注意:递归过程中需要维护一个栈,用于标记哪些人缺物品,每次递归需要把发起人压入栈中;往更深递归时不能再回头向这些栈中的人索要物品(这个由一个vis数组实现)不然的话会形成回路,即A向B要物品,B向C要物品,C向D要物品,D又向栈中的B要物品,B又向C要……就无限循环了

⭐️ 所以,什么时候一个(我们所考虑的)新人能获得一个心仪的物品呢?那就是:要么直接有一个他喜爱的物品未被占有;要么他向一个人P要一个他喜爱的物品,这个人P向另一个人Q要,Q又向R要,……,直到Y向Z要,且Z恰好可以重新占有一个他喜爱的空闲物品的时候(前者可看作后者的一个特殊情况)。这个链条就是大名鼎鼎的增广路,可以表示为:(比如)A-1-B-2-C-3-…-H-8。增广路一定有奇数条边,其中A-1的含义是“人A想要抢夺物品1”,1-B的含义是“因为B目前占有物品1,所以他被逼着找别的物品”;最后H-8代表“终于,人H找到了另一个心爱的物品8,其中8目前是空闲状态”。如果有一个人A找到了由他开始的一条增广路,那么他就可以分配到物品。上面提到的递归过程就是一个寻找可行增广路的过程。

⭐️ 总结一下,目前的算法流程是:按任意顺序扫描每个人,对于每个人通过递归方法求出他能不能通过找到一条从他开始的增广路来分配到一个物品;在递归过程中不能回过头问栈中的人要物品,因为他们本来就是缺物品的状态,如果这样会产生无限循环。

🤔 但是,这样的算法实现出来在洛谷上会有三个点TLE。有一个值得注意的细节是:我们只是禁止向递归栈中的人索取物品,但是如果一条递归路径走投无路回溯的时候,一些人会被弹出栈外。这意味着递归过程可能访问同一个人多次(因为他被弹出栈后就可以变成被访问了;换言之,vis数组中他可能重新被标记为未访问状态)。

😕 你可能会问:难道我们可以只访问每个人一次吗?难道不同的栈格局访问同一个人,是否能形成合法增广路的结果是一样的吗? ——说实话,这个问题我想了很久。

💡 还真是。在我后面的代码中,每次扫描到新的人重置一遍vis数组,且递归到某个人直接令vis[他]=true,如果已经为true则直接返回增广路寻找失败。这是我觉得这个算法最玄妙的地方——如果在某个栈格局下寻找以人P开始的增广路失败,那我们在后面就直接放弃这个人了(即便在另一个栈格局下从P开始可能会成功)。那么我们自然要问:这样不会漏掉一些可行的增广路,导致算法在本该返回成功的时候返回失败吗?

🌈 想要证明不会漏掉情况,就要证明:如果有一个被vis[P]=true排除掉的成功增广路 β \beta β,那么一定存在另一个不含P的成功增广路 α \alpha α,使得我们的算法可以发现该增广路 α \alpha α并返回成功。(这样我们就能绕开“P被禁止访问了”这个限制。)在这之前,我们还因为在栈格局 γ \gamma γ(即一个残缺的、失败的增广路)下遇到P无功而返,而把P标记为不可访问(就算这时把vis[P]标记为true的)。这个失败的栈格局 γ \gamma γ也非常重要。
首先, β \beta β中遇到P后并未无功而返,而是成功找到了后续的增广路。那为什么在 γ \gamma γ下却失败了呢?只有一种解释: β \beta β中有P后面的人在 γ \gamma γ的前缀中出现过,但在 γ \gamma γ下因为不能回头问栈中的人要物品所以就不可访问了。
现在,我要开始施展魔法了:取 β \beta β中P后面最后一个出现在 γ \gamma γ前缀中的人T,并把 γ \gamma γ中T后面的部分替换为 β \beta β中T后面的部分,得到 α \alpha α。那么这个 α \alpha α一定是合法增广路:因为 β \beta β中T后面的部分都没有在 γ \gamma γ前缀中出现过的人了(毕竟T已经是最后一个出现的了),而 α \alpha α继承了 γ \gamma γ的一部分前缀和 β \beta β的一部分后缀,所以 α \alpha α中并没有同一个人被访问两次的情况。况且 α \alpha α是不含P的,所以我们的算法不会把 α \alpha α排除出去。
当然你可能会说:如果 α \alpha α也因为另一个人Q被标记为vis[Q]=true而被排除了呢?那就把 α \alpha α当作新的 β \beta β,“排除Q的失败增广路前缀”为新的 γ \gamma γ,重复前后缀拼接的操作,……直到迭代了若干次后的 α \alpha α没有被排除为止。至此,我们便证明了每个人在每一轮中只被访问一次的合理性。这也就保证了算法的复杂度为 O ( ∣ V ∣ ∣ E ∣ ) O(|V||E|) O(V∣∣E)(即人数与边数之积):每个人被扫描到;每次扫描一个人,可以保证所有的人都只被访问一次,而每次访问可能会检查他所有喜欢的物品(即他的所有边),故每次扫描至多访问所有边。


💻 代码实现:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cstdlib>
#include <queue>

using namespace std;

const int MAXN = 1005;
vector<int> e[MAXN];
int n, m, t, match[MAXN], vis[MAXN];

bool dfs(int u)
{
    if(vis[u]) return false;
    vis[u] = true;
    for(int it = 0; it < e[u].size(); ++it)
    {
        int v = e[u][it];
        if(!match[v] || dfs(match[v]))
        {
            match[v] = u;
            return true;
        }
    }
    return false;
}

int solve()
{
    int ans = 0;
    for(int i = 1; i <= n; ++i)
    {
        memset(vis, '\0', sizeof(vis));
        ans += dfs(i);
    }
    return ans;
}

int main()
{
    cin >> n >> m >> t;
    int u, v;
    while(t--)
    {
        cin >> u >> v;
        e[u].push_back(v);
    }
    cout << solve() << endl;
    return 0;
}

参考:https://cp-algorithms.com/graph/kuhn_maximum_bipartite_matching.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2379507.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Text2SQL:自助式数据报表开发---0517

Text2SQL技术 早期阶段&#xff1a;依赖于人工编写的规则模板来匹配自然语言和SQL语句之间的对应关系 机器学习阶段&#xff1a;采用序列到序列模型等机器学习方法来学习自然语言与SQL之间的关系 LLM阶段&#xff1a;借助LLM强大的语言理解和代码生成能力&#xff0c;利用提示…

使用Visual Studio将C#程序发布为.exe文件

说明 .exe 是可执行文件&#xff08;Executable File&#xff09;的扩展名。这类文件包含计算机可以直接运行的机器代码指令&#xff0c;通常由编程语言&#xff08;如 C、C、C#、Python 等&#xff09;编译或打包生成。可以用于执行自动化操作&#xff08;执行脚本或批处理操…

React Flow 边的基础知识与示例:从基本属性到代码实例详解

本文为《React Agent&#xff1a;从零开始构建 AI 智能体》专栏系列文章。 专栏地址&#xff1a;https://blog.csdn.net/suiyingy/category_12933485.html。项目地址&#xff1a;https://gitee.com/fgai/react-agent&#xff08;含完整代码示​例与实战源&#xff09;。完整介绍…

oracle 资源管理器的使用

14.8.2资源管理器的使用 资源管理器控制CPU资源使用说明&#xff1a;  第一种分配方法&#xff1a;EMPHASIS CPU 分配方法确定在资源计划中对不同使用者组中的会话的重视程度。CPU占用率的分配级别为从1 到8&#xff0c;级别1 的优先级最高。百分比指定如何将CPU 资源分配给每…

贝叶斯优化Transformer融合支持向量机多变量回归预测,附相关性气泡图、散点密度图,Matlab实现

贝叶斯优化Transformer融合支持向量机多变量回归预测&#xff0c;附相关性气泡图、散点密度图&#xff0c;Matlab实现 目录 贝叶斯优化Transformer融合支持向量机多变量回归预测&#xff0c;附相关性气泡图、散点密度图&#xff0c;Matlab实现效果一览基本介绍程序设计参考资料…

Docker配置SRS服务器 ,ffmpeg使用rtmp协议推流+vlc拉流

目录 演示视频 前期配置 Docker配置 ffmpeg配置 vlc配置 下载并运行 SRS 服务 推拉流流程实现 演示视频 2025-05-18 21-48-01 前期配置 Docker配置 运行 SRS 建议使用 Docker 配置 Docker 请移步&#xff1a; 一篇就够&#xff01;Windows上Docker Desktop安装 汉化完整指…

一个stm32工程从底层上都需要由哪些文件构成

原文链接&#xff1a;https://kashima19960.github.io/2025/05/17/stm32/一个stm32工程从底层上都需要由哪些文件构成/ 前言 我最近因为做课设要用到stm32&#xff0c;所以去找了一些开源的stm32工程来看看&#xff0c;然后发现现在新版的keil mdk对于环境的配置跟以前 相比发…

[Mac] 开发环境部署工具ServBay 1.12.2

[Mac] 开发环境部署工具ServBay 链接&#xff1a;https://pan.xunlei.com/s/VOQS0LDsC_J6XU4p-R6voF6YA1?pwdnbyg# 非常给力的本地 Web 开发/测试环境工具&#xff1a;ServBay。之前我们本地搭个 PHP MySQL Nginx 环境&#xff0c;或者搞个 PHP web 环境啥的&#xff0c;不…

商城小程序源码介绍

今天要为大家介绍一款基于ThinkPHP、FastAdmin以及UniApp开发的商城小程序源码&#xff0c;这款源码在设计和功能上都有不俗的表现&#xff0c;非常适合想要搭建线上商城的开发者。 该源码采用了ThinkPHP作为后端框架&#xff0c;利用其强大的性能和灵活性&#xff0c;保障了系…

科技项目验收测试对软件产品和企业分别有哪些好处?

科技项目验收测试是指在项目的开发周期结束后&#xff0c;针对项目成果进行的一系列验证和确认活动。其目的是确保终交付的产品或系统符合预先设定的需求和标准。验收测试通常包括功能测试、性能测试、安全测试等多个方面&#xff0c;帮助企业评估软件在实际应用中的表现。 科…

汽车零部件冲压车间MES一体机解决方案

在当前制造业升级的大背景下&#xff0c;提升生产效率、实现精细化管理已成为企业竞争力的关键。特别是在汽车零部件制造领域&#xff0c;冲压车间作为生产流程中的重要一环&#xff0c;其生产数据的实时采集与分析对于确保产品质量、优化生产节拍、降低运营成本至关重要。今天…

hysAnalyser 从MPEG-TS导出ES功能说明

摘要 hysAnalyser 是一款特色的 MPEG-TS 数据分析工具。本文主要介绍了 hysAnalyser 从MPEG-TS 中导出选定的 ES 或 PES 功能(版本v1.0.003)&#xff0c;以便用户知悉和掌握这些功能&#xff0c;帮助分析和解决各种遇到ES或PES相关的实际问题。hysAnalyser 支持主流的MP1/MP2/…

家里wifi不能上网或莫名跳转到赌博及色情网站就是域名被劫持、DNS被污染了

文章目录 定义上网过程域名被劫持可能阶段案例排查工具 解决方法清除系统DNS缓存查看DNS缓存清除DNS缓存 登录路由器&#xff0c;设置DNS可用的DNS地址&#xff1a; 找网络运营商报警 定义 DNS&#xff08;Domain Name System&#xff0c;域名系统&#xff09;劫持&#xff0c…

基于SSM实现的健身房系统功能实现十六

一、前言介绍&#xff1a; 1.1 项目摘要 随着社会的快速发展和人们健康意识的不断提升&#xff0c;健身行业也在迅速扩展。越来越多的人加入到健身行列&#xff0c;健身房的数量也在不断增加。这种趋势使得健身房的管理变得越来越复杂&#xff0c;传统的手工或部分自动化的管…

【Java微服务组件】分布式协调P1-数据共享中心简单设计与实现

欢迎来到啾啾的博客&#x1f431;。 记录学习点滴。分享工作思考和实用技巧&#xff0c;偶尔也分享一些杂谈&#x1f4ac;。 欢迎评论交流&#xff0c;感谢您的阅读&#x1f604;。 目录 引言设计一个共享数据中心选择数据模型键值对设计 数据可靠性设计持久化快照 &#xff08…

cursor/vscode启动项目connect ETIMEDOUT 127.0.0.1:xx

现象&#xff1a; 上午正常使用cursor/vscode&#xff0c;因为需要写前端安装了nodejs16.20和vue2&#xff0c;结果下午启动前端服务无法访问&#xff0c;浏览器一直转圈。接着测试运行最简单的flask服务&#xff0c;vscode报错connect ETIMEDOUT 127.0.0.1:xx&#xff0c;要么…

兼顾长、短视频任务的无人机具身理解!AirVista-II:面向动态场景语义理解的无人机具身智能体系统

作者&#xff1a;Fei Lin 1 ^{1} 1, Yonglin Tian 2 ^{2} 2, Tengchao Zhang 1 ^{1} 1, Jun Huang 1 ^{1} 1, Sangtian Guan 1 ^{1} 1, and Fei-Yue Wang 2 , 1 ^{2,1} 2,1单位&#xff1a; 1 ^{1} 1澳门科技大学创新工程学院工程科学系&#xff0c; 2 ^{2} 2中科院自动化研究所…

20250515配置联想笔记本电脑IdeaPad总是使用独立显卡的步骤

20250515配置联想笔记本电脑IdeaPad总是使用独立显卡的步骤 2025/5/15 19:55 百度&#xff1a;intel 集成显卡 NVIDIA 配置成为 总是用独立显卡 百度为您找到以下结果 ?要将Intel集成显卡和NVIDIA独立显卡配置为总是使用独立显卡&#xff0c;可以通过以下步骤实现?&#xff…

sparkSQL读入csv文件写入mysql

思路 示例 &#xff08;年龄>18改成>20) mysql的字符集问题 把user改成person “让字符集认识中文”

大涡模拟实战:从区域尺度到街区尺度的大气环境模拟

前言&#xff1a; 随着低空经济的蓬勃发展&#xff0c;无人机、空中出租车等新型交通工具正在重塑我们的城市空间。这场静默的革命不仅带来了经济机遇&#xff0c;更对城市大气环境提出了全新挑战。在距离地面200米以下的城市冠层中&#xff0c;建筑物与大气的复杂相互作用、人…