当前位置: 首页 > news >正文

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

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.执行结果

相关文章:

  • ChatOn:智能AI聊天助手,开启高效互动新时代
  • 摩尔投票算法原理实现一文剖析
  • NodeJS全栈WEB3面试题——P3Web3.js / Ethers.js 使用
  • vulnyx loweb writeup
  • Axure 基础入门
  • EXSI通过笔记本wifi上外网配置
  • PHP舆情监控分析系统(9个平台)
  • 数据结构与算法:图论——拓扑排序
  • 【多线程初阶】内存可见性问题 volatile
  • 电子电路:4017计数器工作原理解析
  • Arbitrum Stylus 合约实战 :Rust 实现 ERC721
  • 【DBA】MySQL经典250题,改自OCP英文题库中文版(2025完整版)
  • 【LLM vs Agent】从语言模型到智能体,人工智能迈出的关键一步
  • 《深入解析SPI协议及其FPGA高效实现》-- 第二篇:SPI控制器FPGA架构设计
  • Kafka集成Flume/Spark/Flink(大数据)/SpringBoot
  • Flink CDC将MySQL数据同步到数据湖
  • 结构型设计模式之装饰模式
  • TDengine 高级功能——读缓存
  • 3,信号与槽机制
  • React 18新特性介绍
  • 成都市建设监理协会网站/百度seo关键词优化软件
  • 重庆网站建设letide/国内最好用免费建站系统
  • 淘宝联盟上怎么建设网站/上海外包seo
  • 网站个性化/优化网站seo公司
  • 长沙网站备案拍照点/seo概念
  • 沧州企业网站制作的/网站推广优化教程