LeetCode LCP 04. 覆盖【二分图最大匹配,匈牙利算法】困难

news2024/9/30 19:33:50

本文属于「征服LeetCode」系列文章之一,这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁,本系列将至少持续到刷完所有无锁题之日为止;由于LeetCode还在不断地创建新题,本系列的终止日期可能是永远。在这一系列刷题文章中,我不仅会讲解多种解题思路及其优化,还会用多种编程语言实现题解,涉及到通用解法时更将归纳总结出相应的算法模板。

为了方便在PC上运行调试、分享代码文件,我还建立了相关的仓库:https://github.com/memcpy0/LeetCode-Conquest。在这一仓库中,你不仅可以看到LeetCode原题链接、题解代码、题解文章链接、同类题目归纳、通用解法总结等,还可以看到原题出现频率和相关企业等重要信息。如果有其他优选题解,还可以一同分享给他人。

由于本系列文章的内容随时可能发生更新变动,欢迎关注和收藏征服LeetCode系列文章目录一文以作备忘。

你有一块棋盘,棋盘上有一些格子已经坏掉了。你还有无穷块大小为1 * 2的多米诺骨牌,你想把这些骨牌不重叠地覆盖在完好的格子上,请找出你最多能在棋盘上放多少块骨牌?这些骨牌可以横着或者竖着放。

输入:n, m 代表棋盘的大小;broken是一个b * 2的二维数组,其中每个元素代表棋盘上每一个坏掉的格子的位置。

输出:一个整数,代表最多能在棋盘上放的骨牌数。

示例 1:

输入:n = 2, m = 3, broken = [[1, 0], [1, 1]]
输出:2
解释:我们最多可以放两块骨牌:[[0, 0], [0, 1]]以及[[0, 2], [1, 2]]。(见下图)


示例 2:

输入:n = 3, m = 3, broken = []
输出:4
解释:下图是其中一种可行的摆放方式


限制:

  1. 1 <= n <= 8
  2. 1 <= m <= 8
  3. 0 <= b <= n * m

解法 匈牙利算法

看了标签才做出来,事后诸葛亮还能看出一些使用二分图的迹象:

  • 骨牌是1x2的大小,一个骨牌的两个位置一定是相邻的两个位置,这两个位置的下标之和的奇偶性相反。这就抽象成了一个二分图
  • 把奇数点看做男士,偶数点看做女士,建图后就比较直观。
  • ==要放置最多的多米诺骨牌,也就是找到一种方式,使得二分图中奇数点和偶数点连起来的边数量最大 ==,这就是典型的二分图最大匹配

因此,这题就是自行建图后使用匈牙利算法的模板题。

以示例一为例建图:

二分图最大匹配问题,一般可以用匈牙利算法解决。在介绍匈牙利算法之前,需要明确一些专有名词:

  • 匹配集合:我们最终的目标是最大化边的数量,这些边将加入匹配集合
  • 匹配边、匹配点:在二分图中,如果本次将两个点连成的边加入匹配集合,就说我们当前将这条边作为了匹配边,边的两个端点均称作匹配点。
  • 未匹配边、未匹配点:在二分图中,如果一个点有一条以上的边,并且其中某一条边已经被加入了匹配集合成为了匹配边,那么剩余的边均称作未匹配边,这些边的另一个端点称为未匹配点。
  • 增广路:以未匹配边开始和结束,且未匹配边与匹配边交替出现的路径。

为了便于大家理解,通过下图(红框和篮框分别表示二分图中的两部分,黑圆表示不同的点。黄和绿线都表示点之间的边)来解释上面 4 4 4 个概念:
300

  • 首先将 1 1 1 号点和 5 5 5 号点之间的边放入匹配集合,该边就变成了匹配边(黄色标识)。 1 1 1 5 5 5 号点就均变为了匹配点,此时,这两个点连接的其他边 ( 1 , 7 ) , ( 2 , 5 ) , ( 4 , 5 ) (1,7),(2,5),(4,5) (1,7),(2,5),(4,5) 就称作未匹配边,对应的点 2 , 4 , 7 2, 4, 7 2,4,7 就均称作未匹配点。
  • 其次将 3 3 3 号点和 6 6 6 号点之间的边放入匹配集合,该边就变成了匹配边,这两个点变为了匹配点。由于这两个点没有连其他的边,所以不会出现新的未匹配边
  • 此时发现,路径 2 − 5 − 1 − 7 2-5-1-7 2517 就是一条 未匹配边-匹配边-未匹配边 组合的增广路径。

明确了这些概念后,看匈牙利算法:

  1. 初始时,最大匹配集合为空。
  2. 我们先找到一组匹配边,加入匹配集合。
  3. 如果找到一条增广路径,就将其中的所有匹配边变为未匹配边,将所有的未匹配边变为匹配边。
  4. 循环步骤 3 3 3 ,直到图中不存在增广路径。算法结束。

匈牙利算法中,最重要的便是步骤 3 3 3 。深入理解——对于一条增广路径,根据其定义,必定含有 k + 1 k + 1 k+1 条未匹配边以及 k k k 条匹配边。那么,步骤 3 3 3 的作用,其实就是将未匹配边和匹配边互换,这样,==该路径上就会更新为 k k k 条未匹配边以及 k + 1 k + 1 k+1 条匹配边,匹配边的数量就比互换之前多了 1 1 1 ==。
500
结合刚才的图片来看:我们将增广路径 2 − 5 − 1 − 7 2-5-1-7 2517 上的未匹配边 ( 2 , 5 ) , ( 1 , 7 ) (2,5),(1,7) (2,5),(1,7) 变为匹配边,将匹配边 ( 5 , 1 ) (5,1) (5,1) 变为未匹配边,图中总匹配边数就从原来的两条 ( 1 , 5 ) , ( 3 , 6 ) (1,5),(3,6) (1,5),(3,6) 变成了三条 ( 2 , 5 ) , ( 1 , 7 ) , ( 3 , 6 ) (2, 5), (1, 7), (3, 6) (2,5),(1,7),(3,6)


一开始建二分图时,我们需要将题目给定的图标识成二分图(比如一部分标识为 0 0 0 ,另一部分标识为 1 1 1 )。但在本题中,棋盘上第 i i i 行第 j j j 列属于哪一部分可以直接根据 i + j i+j i+j 的奇偶性得到。

特别地,在二分图中,只需要从一个集合向另一个集合连有向边即可,不需要双向连边(虽然代码中随手写的双向连边)。另外,本题中棋盘上有些点不可以放多米诺骨牌,在连边过程中进行特判即可。

class Solution { 
public:
    int domino(int n, int m, vector<vector<int>>& broken) {
        vector<int> g[100];
        bool vis[100] = {false};
        bool b[100] = {false};
        int match[100];
        int ans = 0; 
        function<bool(int)> dfs = [&](int u) -> bool {
            for (int v : g[u]) { 
                if (!vis[v]) { // 没访问过
                    vis[v] = true; // 避免重复访问, 能让就让
                    if (match[v] == -1 || dfs(match[v])) {
                        match[v] = u; return true;
                    }
                }  
            }
            return false;
        };
        for (vector<int> &bv : broken) // 哪些位置破损 
            b[bv[0] * m + bv[1]] = true; 
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < m; ++j) {
                int u = i * m + j;
                if (b[u]) continue;
                int v1 = i * m + j + 1, v2 = (i + 1) * m + j;
                if (j + 1 < m && !b[v1]) { // 只存奇数点到偶数点的边也行
                    g[u].push_back(v1);
                    g[v1].push_back(u);
                }
                if (i + 1 < n && !b[v2]) { 
                    g[u].push_back(v2);
                    g[v2].push_back(u);
                }
            }
        }
        memset(match, -1, sizeof(match));
        for (int i = 0, t = n * m; i < t; ++i) {
            int x = i / m, y = i % m;
            memset(vis, false, size(vis));
            if (((x + y) & 1) && !b[i] && !vis[i] && dfs(i)) // 从奇数点出发向偶数点连边
                ++ans;
        }
        return ans;
    }
};

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

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

相关文章

400元左右的蓝牙耳机啥牌子好?400元价位蓝牙耳机推荐

随着人们越来越倾向于使用随身便携的电子产品&#xff0c;轻松上阵、无线自由的TWS蓝牙耳机越来越受消费者的青睐&#xff0c;成为现在耳机行业的新星&#xff0c;下面整理了几款400元价位的耳机品牌。 一、南卡小音舱Lite2蓝牙耳机 参考价格&#xff1a;239元 佩戴方式&…

AgentAI+ChatGPT给出答案-为什么即时通讯需要心跳

序言 人工智能ChatGpt 结合系统化的问题拆解, 现在已经能够进行问题的拆解与自问自答, 预计未来很多的脑力工作要被释放了, 作为即时通讯的开发人员, 我问问专业的问题 为什么即时通讯需要心跳 先看产品界面与使用结果 问题拆解过程 执行任务1: 概念搜索 “Executing “Res…

【hello Linux】基础IO

目录 1.C语言文件操作 1. 打开文件&#xff1a; 2. 文件操作 3. 关闭文件 2. C语言中的流操作 3. 系统文件IO 1. 接口介绍 2. 写文件 3. 读文件 4. 文件描述符fd 0 & 1 & 2 文件描述符&#xff1a; 文件描述符原理&#xff1a; 文件描述符的分配规则&#xff1a; 5. 重…

企业数字化转型如何做?看过来

一、什么是数字化转型&#xff1f; 企业数字化转型旨在以数字化技术为基础&#xff0c;建立一个与物理世界对应的数字世界。在数字化转型过程中&#xff0c;数据是核心&#xff0c;人工智能是手段&#xff0c;云化服务是形式&#xff0c;企业的组织制度、流程优化与重构及人才…

ROS学习——艰辛的环境安装之路一ROS安装Kinetic版本

ROS-Kinetic Kinetic版本的ROS是用在Ubuntu16.04版本下的&#xff0c;先确认Ubuntu版本 Kinetic安装的官方文档&#xff08;可以按照原文来&#xff09; http://wiki.ros.org/kinetic/Installation/Ubuntu 1.配置Ubuntu的软件仓库 在安装 Ubuntu 的过程中如果你系统选了中文…

电子表格 VS 数据网格,你的React应用程序应该怎么选?

电子表格和数据网格&#xff1a;乍一看他们似乎是可互换的组件&#xff0c;由于两者都是用表格显示来格式化大量数据&#xff0c;因此很难知道应该为React应用程序选择哪个选项。 尽管它们的外观相似&#xff0c;但提供了非常不同的功能——选择错误的一个将对用户体验产生负面…

vite面试题

为什么说vite比webpack更快 和webpack对比&#xff0c;为什么 vite 的冷启动、热启动、热更新都会快&#xff1f;这就要说说二者的区别。 使用 webpack 时&#xff0c;从 yarn start 命令启动&#xff0c;到最后页面展示&#xff0c;需要经历的过程&#xff1a; 以 entry 配置…

如何制作订单工序流转报表

使用外部数据分析工具&#xff0c;比如百度Sugar&#xff0c;连接草料二维码官方数据库&#xff0c;即可制作自定义可视化报表。本文会具体介绍订单工序流转报表的制作流程。 一、案例效果 订单工序流转报表可以帮助管理者快速查询各订单的最新进展、是否超期&#xff0c;关注…

SAP Web IDE本地环境搭建

SAP Web IDE本地环境搭建 1、JDK的安装及配置环境变量 可通过官网自行下载JDK&#xff1a; Java Downloads | Oracle 直接傻瓜式安装至默认路径“C:\Program Files\Java\jdk-17.0.2”&#xff0c;然后配置环境变量“JAVA_HOME”、“CLASSPATH”和“PATH”。 JAVA_HOME&…

MySQL的基础学习

文章目录 一、MySQL NULL 值处理二、MySQL 正则表达式三、MySQL 事务四、MySQL ALTER命令总结 一、MySQL NULL 值处理 MySQL 使用 SQL SELECT 命令及 WHERE 子句来读取数据表中的数据,但是当提供的查询条件字段为 NULL 时&#xff0c;该命令可能就无法正常工作。 为了处理这种…

HTTP常用请求头和响应头有哪些?

一、什么是http 超文本传输协议&#xff08; Hypertext Transfer Protocol &#xff0c;HTTP&#xff09;的请求和响应消息中&#xff0c;协议头部分的那些组件。用来准确描述正在获取的资源、服务器或者客户端的行为&#xff0c;定义了HTTP事务中的具体操作参数。 二、什么是…

webGL前端数字孪生技术方案

本篇内容本是为公司内部分享会准备的大纲&#xff0c;发出来给想做webGL的前端做个参考。 课程介绍&#xff1a; 基于webGL技术&#xff0c;实现数字孪生应用。讲解从模型绘制、webGL框架、数据通讯等方案的技术选型。初步了解图形学的基础内容&#xff0c;熟悉webGL的开发流程…

瀚高股份吕新杰:创新开源双驱动 躬耕国产数据库

近年来&#xff0c;国际形势不断变幻&#xff0c;也给人们带来巨大警示&#xff1a;关键核心技术是买不来、讨不来的&#xff0c;中国科技企业需寻找研发自强之路。 瀚高基础软件股份有限公司&#xff08;简称瀚高股份&#xff09;专注数据库十八年&#xff0c;始终以“振兴民…

【技巧分享】Nacos未经授权的登录漏洞-任意密码直接登录后台

文章目录 前言一、nacos简介二、漏洞复现总结 前言 碰到个很有意思的系统&#xff0c;Nacos。通过查找资料&#xff0c;发现Nacos < 2.1.0 版本都存在这个漏洞。 漏洞描述&#xff1a;Nacos中发现影响Nacos < 2.1.0的问题&#xff0c;Nacos用户使用默认JWT密钥导致未授…

好用还免费的10个Figma插件推荐

Figma是一款广受设计师喜爱的工具&#xff0c;可用于创建和协作用户界面、图形和原型。它广泛应用的原因之一是因为有许多插件可供选择&#xff0c;可以增强其功能。在本文中&#xff0c;我们将探讨10个设计师必备的Figma插件。如果你无法使用Figma 可以使用即时设计——即时设…

OpenAI最新官方ChatGPT聊天插件接口《智能聊天插件引言》全网最详细中英文实用指南和教程,助你零基础快速轻松掌握全新技术(一)(附源码)

Chat Plugins Limited Alpha 聊天插件 前言IntroductionPlugin flow 插件流其它资料下载 Learn how to build a plugin that allows ChatGPT to intelligently call your API. 了解如何构建允许ChatGPT智能调用API的插件。 前言 在现代的软件开发环境中&#xff0c;使用第三方…

Doris使用总结

场景 Apache Doris是由百度贡献的开源MPP分析型数据库产品&#xff0c;亚秒级查询响应时间&#xff0c;支持实时数据分析&#xff1b;分布式架构简洁&#xff0c;易于运维&#xff0c;可以支持10PB以上的超大数据集&#xff1b;可以满足多种数据分析需求&#xff0c;例如固定历…

Maven(六):Maven的使用——继承与聚合

Maven&#xff08;六&#xff09;&#xff1a;Maven的使用——继承与聚合 前言一、实验九&#xff1a;继承1、概念2、作用3、举例4、操作4.1 创建父工程4.2 创建模块工程4.3 查看被添加新内容的父工程 pom.xml4.4 解读子工程的pom.xml4.5 在父工程中配置依赖的统一管理4.6 子工…

电脑端(PC)按键精灵——5.找色/找图命令

电脑端(PC)按键精灵——5.找色/找图命令 注&#xff1a;说了键盘、鼠标、其他、控制命令还有安装内容&#xff0c;现在说下颜色/图形命令&#xff0c;这一节相当重要 按键精灵小白入门详细教程&#xff1a; 电脑端(PC)按键精灵—小白入门 详细教程 命令介绍 1.GetPixelCol…

人大金仓携手深信服打造更高效、更融合一体化平台联合解决方案

近日&#xff0c;人大金仓数据库与深信服超融合平台完成深度优化&#xff0c;表现出性能更优、运行更稳定、数据更安全。双方联合打造更高效、更融合的一体化平台联合解决方案&#xff0c;能更好地满足行业信创升级需求。 随着新一轮信创政策陆续出台&#xff0c;信创产业从党政…