Leetcode 2123. 使矩阵中的 1 互不相邻的最小操作数

article/2025/7/2 14:10:25

1.题目基本信息

1.1.题目描述

给你一个 下标从 0 开始 的矩阵 grid。每次操作,你可以把 grid 中的 一个 1 变成 0 。

如果一个矩阵中,没有 1 与其它的 1 四连通(也就是说所有 1 在上下左右四个方向上不能与其他 1 相邻),那么该矩阵就是 完全独立 的。

请返回让 grid 成为 完全独立 的矩阵的 最小操作数。

1.2.题目地址

https://leetcode.cn/problems/minimum-operations-to-remove-adjacent-ones-in-matrix/description/

2.解题方法

2.1.解题思路

二分图+匈牙利算法求二分图的最大匹配数

二分图+Hopcroft-karp算法求二分图的最大匹配数

2.2.解题步骤

二分图+匈牙利算法步骤

  • 第一步,构建二分图。在所有值为1的结点中,其中偶数下标行的偶数下标结点和奇数下标行的奇数下标结点为左结点集合uSet,其余结点为右结点集合vSet;uSet和vSet中彼此相邻代表一条边

  • 第二步,执行匈牙利算法获取最大匹配数

    • 匈牙利算法的步骤请看代码注释

二分图+Hopcroft-karp算法步骤

  • 第一步,构建二分图。在所有值为1的结点中,其中偶数下标行的偶数下标结点和奇数下标行的奇数下标结点为左结点集合uSet,其余结点为右结点集合vSet;uSet和vSet中彼此相邻代表一条边

  • 第二步,执行二分图+Hopcroft-karp算法获取最大匹配数

    • Hopcroft-karp算法的步骤请看代码注释

3.解题代码

二分图+匈牙利算法代码

# ==> 匈牙利算法
# params:graph:二分图的邻接表;leftNodes:二分图左集合的所有结点数组
# return:返回二分图中的最大匹配数(等于最小点覆盖数)
from collections import defaultdict
def hungarian(graph:dict[int,list[int]], leftNodes:list[int]) -> int:# 第一步,构建维护变量。rightMatch维护右集合中各个结点的匹配状态rightMatch = {}# 第二步,构建dfs递归函数。递归任务:判断左集合中结点u是否能成功匹配。如果u相邻结点v没有匹配,则直接匹配;如果v已经匹配,则将v标记为已访问,dfs(match[v])观察是否能让match[v]结点匹配非v结点来释放v结点,让u进行匹配,即贪心地进行匹配。def dfs(u:int) -> bool:# 2.1.枚举u的相邻的右集合结点vfor v in graph[u]:# 2.2.判断v是否已访问,已访问则跳过if not visited[v]:# 2.3.标记v为已访问,并根据条件判断是否能匹配,能匹配的情况下记录右结点v的匹配状态到rightMatch中visited[v] = Trueif v not in rightMatch or dfs(rightMatch[v]):rightMatch[v] = ureturn True# 2.4.如果最终都没能匹配一个,则返回Falsereturn False# 第三步,枚举每一个左集合结点u,调用dfs判断各个u结点是否能成功进行匹配,并记录最大匹配数到ans中ans = 0for u in leftNodes:visited = defaultdict(bool)if dfs(u):ans += 1return ansclass Solution:def getGraphAndNodes(self, grid: List[List[int]]):m, n = len(grid), len(grid[0])# 第一步,构建二分图。在所有值为1的结点中,其中偶数下标行的偶数下标结点和奇数下标行的奇数下标结点为左结点集合uSet,其余结点为右结点集合vSet;uSet和vSet中彼此相邻代表一条边graph = defaultdict(list)leftNodes, rightNodes = [], []for i in range(m):for j in range(n):if grid[i][j] == 1:# 1.1.将结点添加到图中node = i * n + jif (i % 2 == 0 and j % 2 == 0) or (i % 2 == 1 and j % 2 == 1):leftNodes.append(node)else:rightNodes.append(node)graph[node] = []# 1.2.将边添加到图中for dr, dc in [[-1, 0], [0, -1], [1, 0], [0, 1]]:nr, nc = i + dr, j + dcif 0 <= nr < m and 0 <= nc < n and grid[nr][nc] == 1:node2 = nr * n + ncgraph[node].append(node2)# print(graph)return graph, leftNodes, rightNodes# 思路1【超时】:二分图+匈牙利算法。题目转换:等价于求二分图的最大匹配数def minimumOperations(self, grid: List[List[int]]) -> int:graph, leftNodes, _ = self.getGraphAndNodes(grid)# 第二步,执行匈牙利算法获取最大匹配数return hungarian(graph, leftNodes)

二分图+Hopcroft-karp算法代码

# ==> 匈牙利算法
# params:graph:二分图的邻接表;leftNodes:二分图左集合的所有结点数组
# return:返回二分图中的最大匹配数(等于最小点覆盖数)
from collections import defaultdict
def hungarian(graph:dict[int,list[int]], leftNodes:list[int]) -> int:# 第一步,构建维护变量。rightMatch维护右集合中各个结点的匹配状态rightMatch = {}# 第二步,构建dfs递归函数。递归任务:判断左集合中结点u是否能成功匹配。如果u相邻结点v没有匹配,则直接匹配;如果v已经匹配,则将v标记为已访问,dfs(match[v])观察是否能让match[v]结点匹配非v结点来释放v结点,让u进行匹配,即贪心地进行匹配。def dfs(u:int) -> bool:# 2.1.枚举u的相邻的右集合结点vfor v in graph[u]:# 2.2.判断v是否已访问,已访问则跳过if not visited[v]:# 2.3.标记v为已访问,并根据条件判断是否能匹配,能匹配的情况下记录右结点v的匹配状态到rightMatch中visited[v] = Trueif v not in rightMatch or dfs(rightMatch[v]):rightMatch[v] = ureturn True# 2.4.如果最终都没能匹配一个,则返回Falsereturn False# 第三步,枚举每一个左集合结点u,调用dfs判断各个u结点是否能成功进行匹配,并记录最大匹配数到ans中ans = 0for u in leftNodes:visited = defaultdict(bool)if dfs(u):ans += 1return ans# ==> Hopcroft-karp算法(时间复杂度:O(sqrt(V)*E))
# params:graph:二分图的邻接表;leftNodes:二分图左集合的所有结点数组;
# return:返回二分图中的最大匹配数(等于最小点覆盖数)
from collections import deque
def hopcroftKarp(graph:dict[int, list[int]], leftNodes:list[int]) -> int:inf = float("inf")# 第一步,构建维护变量。match_维护二分图中各个匹配关系;dist维护各个交替路中距离左集合中未匹配点的最短距离,dist中映射值为inf代表结点已经匹配或者不存在以该结点开头的增广路径match_ = {}dist = {}# 第二步,构建广搜函数。bfs功能:返回在match_和dist的情况下,是否还存在更多的增广路径,同时更新distdef bfs() -> bool:# 2.1.将左侧集合的未匹配结点全部加入到队列中,作为BFS的起点;同时将左集合中已经匹配的结点的距离值设置为infque = deque()for u in leftNodes:if u not in match_:que.append(u)dist[u] = 0else:dist[u] = inf# 2.2.广搜二分图。使用found维护图中是否还存在增广路径found = Falsewhile que:u = que.popleft()for v in graph[u]:if v not in match_:# 2.2.1.如果右侧结点v没有在match_中,说明v结点还没有匹配,表示图中还存在增广路径,设置found=Truefound = Trueelif dist[match_[v]] == inf:# 2.2.2.如果右侧结点v在match_中且对应的match_[v]结点还没有被广搜到(即dist[match_[v]==inf]),说明v结点已经匹配,则将v的匹配结点match_[v]加入到广搜队列中,继续寻找增广路径;并更新match_[v]结点所在的最小广搜层次que.append(match_[v])dist[match_[v]] = dist[u] + 1# 2.3.返回图中是否还存在增广路径return found# 第三步,构建受限深搜函数。深搜任务:判断以结点u开头是否存在增广路径;如果存在,则在该路径上进行结点匹配,反转该增广路径。def dfs(u:int) -> bool:for v in graph[u]:if v not in match_ or (dist[match_[v]] == dist[u] + 1 and dfs(match_[v])):# 3.1.如果结点v没有匹配,则u->v就是最短的增广路径,直接匹配u和v并返回即可;如果结点v已经匹配了,只有满足match_[v]是广搜中结点u的下一层级时(即dist[match_[v]]==dist[u]+1)(这个条件也能过滤掉递归时match_[v]遍历到相邻结点v的情况),才能继续dfs判断是否存在增广路径,如果存在则匹配并反转该增广路径match_[u] = vmatch_[v] = ureturn True# 3.2.如果不存在以u开头的增广路径,则在dist中进行标记,并返回Falsedist[u] = infreturn False# 第四步,循环地进行bfs判断图中是否存在增广路径;如果存在则遍历所有左侧没有匹配的结点u,dfs寻找是否存在u开头的最短增广路径,并在dfs中更新match_和dist,如果存在以u开头的增广路径,则通过反转增广路径进行匹配,并将ans自增1ans = 0while bfs():for u in leftNodes:if u not in match_ and dfs(u):ans += 1return ansclass Solution:def getGraphAndNodes(self, grid: List[List[int]]):m, n = len(grid), len(grid[0])# 第一步,构建二分图。在所有值为1的结点中,其中偶数下标行的偶数下标结点和奇数下标行的奇数下标结点为左结点集合uSet,其余结点为右结点集合vSet;uSet和vSet中彼此相邻代表一条边graph = defaultdict(list)leftNodes, rightNodes = [], []for i in range(m):for j in range(n):if grid[i][j] == 1:# 1.1.将结点添加到图中node = i * n + jif (i % 2 == 0 and j % 2 == 0) or (i % 2 == 1 and j % 2 == 1):leftNodes.append(node)else:rightNodes.append(node)graph[node] = []# 1.2.将边添加到图中for dr, dc in [[-1, 0], [0, -1], [1, 0], [0, 1]]:nr, nc = i + dr, j + dcif 0 <= nr < m and 0 <= nc < n and grid[nr][nc] == 1:node2 = nr * n + ncgraph[node].append(node2)# print(graph)return graph, leftNodes, rightNodes# 思路2:二分图+Hopcroft-karp算法def minimumOperations(self, grid: List[List[int]]) -> int:graph, leftNodes, rightNodes = self.getGraphAndNodes(grid)# 计算最大匹配数maxMatching = hopcroftKarp(graph, leftNodes)return maxMatching

4.执行结果


http://www.hkcw.cn/article/ytvOiJjSXt.shtml

相关文章

STL解析——list的使用

目录 1.简介 2.构造函数 3.迭代器 3.1封装 3.2迭代器分类 4.排序性能 4.1链式与数组 4.2缓存读取 1.简介 STL容器中提供的list容器也是一种顺序容器&#xff0c;底层实现方式是带头双向链表&#xff0c;这种实现方式能比单链表更高效的访问数据。 下面围绕部分重要接口…

数据库系统概论(十一)SQL 集合查询 超详细讲解(附带例题表格对比带你一步步掌握)

数据库系统概论&#xff08;十一&#xff09;SQL 集合查询 超详细讲解&#xff08;附带例题表格对比带你一步步掌握&#xff09; 前言一、什么是集合查询&#xff1f;二、集合操作的三种类型1. 并操作2. 交操作3. 差操作 三、使用集合查询的前提条件四、常见问题与注意事项五、…

数学建模期末速成 最短路径

关键词&#xff1a;Dijkstra算法 Floyd算法 例题 已知有6个村庄&#xff0c;各村的小学生人数如表所列&#xff0c;各村庄间的距离如图所示。现在计划建造一所医院和一所小学&#xff0c;问医院应建在哪个村庄才能使最远村庄的人到医院看病所走的路最短&#xff1f;又问小学建…

MonitorSDK_监测用户行为(点击、页面路由变化、页面浏览量变化)

点击事件监测 为了实现用户点击事件的监控和数据埋点&#xff0c;可以通过监听全局的 mousedown 和 touchstart 事件&#xff0c;收集用户交互数据&#xff0c;并将其上报到服务器。 export default function onClick(){[mousedown, touchstart].forEach( eventType > { …

NE555输出PWM驱动NMOS控制灯光电路Multisim仿真

仿真电路&#xff1a; 遇到的一些问题&#xff1a; 1、NE555怎么产生PWM波形&#xff1f; 解&#xff1a; 555定时器频率计算器_555定时器频率在线计算_电路参数计算 - 电子发烧友(www.elecfans.com) 这个在线工具可以通过设定频率、占空比、电阻&#xff0c;从而求出电阻值…

ThinkPrune:在RL中引入长度限制,在保持性能一致或略有提升下,显著提升推理效率

摘要&#xff1a;我们提出了THINKPRUNE&#xff0c;这是一种简单而有效的方法&#xff0c;用于缩短长思考型大语言模型&#xff08;LLMs&#xff09;的思考长度。这些模型被发现常常会产生低效且冗余的思考过程。现有的关于减少思考长度的初步探索主要集中在迫使思考过程提前结…

重温经典算法——堆排序

版权声明 本文原创作者&#xff1a;谷哥的小弟作者博客地址&#xff1a;http://blog.csdn.net/lfdfhl 基本原理 堆排序是一种基于二叉堆的排序算法&#xff0c;时间复杂度为O(n log n)。堆排序核心步骤包括构建最大堆和反复取出堆顶元素排序&#xff1a;首先从最后一个非叶子…

PyTorch——卷积层(3)

conv_arithmetic/README.md at master vdumoulin/conv_arithmetic GitHub out_channel1 out_channel2

5.29 自学测试 Linux基础 Day4

一、Linux操作系统介绍 1.操作系统介绍&#xff1a; 管理计算机硬件与软件资源的计算机程序&#xff0c;同时也是计算机系统的内核与基石。 2.常见的操作系统 桌面操作系统&#xff1a;Windows系列、Linux、MacOS 嵌入式操作系统&#xff1a;Linux 服务器操作系统&#x…

推荐一款使用html开发桌面应用的工具——mixone

简介 mixone是开发桌面应用&#xff08;Win、Mac、Linux&#xff09;的一款工具、其基于electron实现。其拥有简单的工程结构。以为熟悉前端开发的程序员可以很轻松的开发出桌面应用&#xff0c;它比electron的其他框架更简单&#xff0c;因为那些框架基本上还需要了解electro…

leetcode hot100 二叉树(二)

书接上回&#xff1a;https://blog.csdn.net/weixin_74129837/article/details/148367615?spm1001.2014.3001.5501 8.验证二叉搜索树 维护一个min_val和max_val&#xff0c;限制当前结点的合法值范围。min_val和max_val动态变化。 class Solution { public:bool check(Tree…

【Linux】基础文件IO

&#x1f31f;&#x1f31f;作者主页&#xff1a;ephemerals__ &#x1f31f;&#x1f31f;所属专栏&#xff1a;Linux 前言 无论是日常使用还是系统管理&#xff0c;文件是Linux系统中最核心的概念之一。对于初学者来说&#xff0c;理解文件是如何被创建、读取、写入以及存储…

MYSQL MGR高可用

1&#xff0c;MYSQL MGR高可用是什么 简单来说&#xff0c;MySQL MGR 的核心目标就是&#xff1a;确保数据库服务在部分节点&#xff08;服务器&#xff09;发生故障时&#xff0c;整个数据库集群依然能够继续提供读写服务&#xff0c;最大限度地减少停机时间。 2. 核心优势 v…

【java面试】MySQL篇

MySQL篇 一、总体结构二、优化&#xff08;一&#xff09;定位慢查询1.1 开源工具1.2Mysql自带的慢日志查询1.3 总结 &#xff08;二&#xff09;定位后优化2.1 优化2.2 总结 &#xff08;三&#xff09;索引3.1 索引3.2 索引底层数据结构——B树3.3 总结 &#xff08;四&#…

头像预览和上传

在写一个项目的时候&#xff0c;遇到了头像修改这个功能的需求&#xff0c;在最开始的学习中发现可以通过type为file的input文件读取图片&#xff0c;然后将其转换为DataUrl格式&#xff0c;最终作为Ima元素的src即可在页面上展示图片。但到后面开始写交互的时候发现DataUrl格式…

解锁效率新高度:Agent Zero智能助手框架

探索Agent Zero AI框架&#xff1a;您的个性化智能助手 在迅速发展的科技世界&#xff0c;Agent Zero AI框架为我们揭开了一个全新的大门。被设计成能够与用户同步成长与学习的智能助手&#xff0c;Agent Zero展现了它作为个性化使用工具的非凡潜力。在本篇文章中&#xff0c;…

第43节:Vision Transformer (ViT)视觉领域的革命性架构

1. ViT的诞生背景与核心思想 Vision Transformer (ViT) 是2020年由Google Research团队提出的一种革命性计算机视觉架构,它将自然语言处理(NLP)领域中大获成功的Transformer模型引入到计算机视觉任务中。这一创新彻底改变了传统卷积神经网络(CNN)在视觉任务中的主导地位,为图…

leetcode0513. 找树左下角的值-meidum

1 题目&#xff1a;找树左下角的值 官方标定难度&#xff1a;中 给定一个二叉树的 根节点 root&#xff0c;请找出该二叉树的 最底层 最左边 节点的值。 假设二叉树中至少有一个节点。 示例 1: 输入: root [2,1,3] 输出: 1 示例 2: 输入: [1,2,3,4,null,5,6,null,null,7]…

从webshell管理工具(蚁剑 冰蝎 哥斯拉 菜刀 哥斯拉等)程控制主机后,再将这个控制能力上线到MSF 为什么要这么做了?一篇文章告诉你

目录 一、为什么Webshell管理工具需要上线到Metasploit&#xff1f; 什么情况下需要上线到Metasploit&#xff1f; 二、常见Webshell管理工具及上线Metasploit的步骤 1. 蚁剑&#xff08;AntSword&#xff09;上线到Metasploit 上线步骤&#xff1a; 实际案例&#xff1a…

【Ragflow】24.Ragflow-plus开发日志:增加分词逻辑,修复关键词检索失效问题

概述 在RagflowPlus v0.3.0 版本推出之后&#xff0c;反馈比较多的问题是&#xff1a;检索时&#xff0c;召回块显著变少了。 如上图所示&#xff0c;进行检索测试时&#xff0c;关键词相似度得分为0&#xff0c;导致混合相似度(加权相加得到)也被大幅拉低&#xff0c;低于设定…