LeetCode 1284. 转化为全零矩阵的最少反转次数
字数 2258 2025-12-07 11:59:03
LeetCode 1284. 转化为全零矩阵的最少反转次数
题目描述
给你一个 m x n 的二进制矩阵 mat,矩阵中的每个元素是 0 或 1。你可以执行一种操作:选中一个格子 (r, c),然后反转这个格子以及所有相邻格子(上、下、左、右,如果存在)的值(0 变 1,1 变 0)。你的目标是将整个矩阵都变成 0。请你返回达成目标所需的最少操作次数。如果无法达成目标,返回 -1。
注意:
- 对同一个格子执行两次操作等价于不执行任何操作,因为会相互抵消。
- 题目保证
1 <= m, n <= 3,矩阵规模非常小。
示例 1
输入:mat = [[0,0],[0,1]]
输出:3
解释:一种可行方案是:
- 翻转 (1,0) → 影响 (0,0) 和 (1,0) → 矩阵变为 [[1,0],[1,1]]
- 翻转 (1,1) → 影响 (0,1) 和 (1,1) → 矩阵变为 [[1,1],[1,0]]
- 翻转 (0,0) → 影响 (0,0) 和 (1,0) → 矩阵变为 [[0,1],[0,0]]
- 实际上,示例解释给出的 3 步操作是另一种翻转顺序,但最少确实是 3 步。
解题思路
这个题目虽然矩阵规模很小(最多 3x3=9 个格子),但可能的操作组合非常多(每个格子可以选择翻转或不翻转)。核心思路是将矩阵状态压缩为一个整数(状态压缩),然后用 BFS 搜索从初始状态到全 0 状态的最短路径。
步骤详解
步骤 1:状态表示
- 矩阵是二进制的,我们可以把矩阵的每一行拼接成一个二进制数。
- 例如,2x2 矩阵
[[a,b],[c,d]]可以表示为二进制数(a b c d),对应的整数是a * 8 + b * 4 + c * 2 + d * 1(如果按行优先)。 - 因为最多 9 个格子,所以状态可以用一个 9 位二进制整数表示,范围是 0 到 511 (2^9-1)。全 0 状态就是 0。
步骤 2:操作的影响
- 对格子 (r, c) 执行翻转操作,会翻转自身和四个相邻格子(如果存在)。
- 这等价于将状态整数与一个“掩码”(mask)进行按位异或(XOR)操作。掩码中,被翻转的位设为 1,其他位设为 0。
- 例如,在 2x2 矩阵中,格子 (0,0) 的掩码是:翻转自身 (0,0) 和相邻的 (0,1)、(1,0)。对应二进制位为:
(1,1,1,0)即整数 14(如果按行优先排列是[0,0]=1<<3, [0,1]=1<<2, [1,0]=1<<1,加起来 8+4+2=14)。
步骤 3:预先计算掩码
- 遍历每个格子 (r, c),计算它的操作掩码。
- 对于每个格子,将自身和上下左右四个邻居(在矩阵范围内)的对应位设为 1。
- 如何计算对应位?可以给每个格子一个编号:
index = r * n + c,那么掩码中对应位就是1 << index。
步骤 4:BFS 搜索最短路径
- 初始状态:将矩阵转换为整数
start。 - 目标状态:整数 0。
- 使用队列进行 BFS,队列中存储 (状态, 步数)。
- 从初始状态开始,尝试对每个可能的操作(即每个格子对应的掩码)进行翻转:
next = state ^ mask。 - 如果
next没有被访问过,就加入队列,并标记已访问。 - 如果遇到状态 0,返回当前步数。
- 如果队列为空还没遇到 0,说明不可达,返回 -1。
步骤 5:为什么 BFS 可行?
- 每次操作都会改变状态,且步长是 1(一次操作)。
- 因为总共只有 512 种状态,BFS 可以在有限时间内完成。
- 注意:BFS 时需要记录已访问状态,防止重复访问。
举例说明
以 mat = [[0,0],[0,1]] 为例:
- 矩阵 2x2,格子编号:
(0,0): index=0, (0,1): index=1,
(1,0): index=2, (1,1): index=3。 - 初始状态:二进制
0 0 0 1即整数 1。 - 预先计算每个格子的掩码:
(0,0):影响 index=0,1,2 → 掩码 = 1<<0 | 1<<1 | 1<<2 = 1+2+4=7
(0,1):影响 index=0,1,3 → 掩码 = 1+2+8=11
(1,0):影响 index=0,2,3 → 掩码 = 1+4+8=13
(1,1):影响 index=1,2,3 → 掩码 = 2+4+8=14 - BFS 过程:
初始状态 1,步数 0。
从状态 1 出发,尝试四种操作:- 1 ^ 7 = 6 (步数 1)
- 1 ^ 11 = 10 (步数 1)
- 1 ^ 13 = 12 (步数 1)
- 1 ^ 14 = 15 (步数 1)
继续从这些状态出发,直到遇到状态 0。可以验证,从状态 1 到状态 0 的最短路径是 3 步。
复杂度分析
- 状态数:最多 2^(m*n) ≤ 512。
- 每个状态尝试 m*n 种操作(最多 9 种)。
- 总时间复杂度 O(2^(mn) m * n),在本题约束下是可行的。
- 空间复杂度 O(2^(m*n)) 存储已访问状态。
总结
这道题的关键在于:
- 状态压缩,将矩阵转为整数。
- 将每个操作转化为与一个掩码的异或。
- 用 BFS 求最短路径,因为操作步长一致。
- 注意小规模下 BFS 是高效的。
通过这种方法,我们可以系统性地搜索所有可能的状态变化,找到最少翻转次数。