🧩【算法题讲解】使网格变为单值网格的最小操作数(Leetcode 风格)
📌 题目描述
给定一个大小为 m x n
的二维整数网格 grid
和一个整数 x
。你可以执行任意次数的操作,每次操作可以选择网格中的任一元素,对其执行加 x
或减 x
的操作。
目标: 通过若干次操作,使得网格中的所有元素相等(即成为“单值网格”)。
返回:
- 如果可以达成目标,返回最小操作数;
- 如果无法达成目标,返回
-1
。
✅ 示例 1:
输入: grid = [[2,4],[6,8]], x = 2
输出: 4
解释:
- 将 2 加 2 -> 4 (1 次操作)
- 将 6 减 2 -> 4 (1 次操作)
- 将 8 减 2 两次 -> 4 (2 次操作)
总共 1+1+2 = 4 次操作
✅ 示例 2:
输入: grid = [[1,5],[2,3]], x = 1
输出: 5
❌ 示例 3:
输入: grid = [[1,2],[3,4]], x = 2
输出: -1
解释: 元素之间差值不是 x 的倍数,无法完成转换。
🔍 解题分析
1. 操作规则限制
题目中的“加 x 或减 x”操作等价于:任意一个数只能向其他数靠拢,前提是它们之间的差值必须是 x
的倍数。
这给出了第一个关键点:
❗️可行性判断:两个数是否可以通过加减 x 互相转换,前提是它们的差值能被 x 整除。
2. 转换为一维问题
将 grid
展平为一维数组后,本质上我们面临的是一个数组中的所有数,是否可以通过加减 x,统一变成某个相同的数。
🧠 解题方法
✅ 步骤总结:
- 展平二维网格为一维列表。
- 判断是否所有数之间差值是 x 的倍数。
- 将所有数“归一化”成以一个参考数(如第一个元素)为基准的操作步数。
- 排序,选中中位数为目标操作步数,以最小化操作总和。
- 计算所有数变成目标所需的总操作次数。
✅ Python 实现代码如下:
from typing import List
class Solution:
def minOperations(self, grid: List[List[int]], x: int) -> int:
# 1. 扁平化
nums = [num for row in grid for num in row]
base = nums[0]
# 2. 可行性判断
for num in nums:
if (num - base) % x != 0:
return -1
# 3. 归一化为步数
ops = [(num - base) // x for num in nums]
ops.sort()
# 4. 选择中位数,最小化总操作数
median = ops[len(ops) // 2]
return sum(abs(op - median) for op in ops)
📊 时间与空间复杂度分析
项目 | 复杂度 |
---|---|
时间复杂度 | O(mn log(mn)) ,排序所致 |
空间复杂度 | O(mn) ,用于保存扁平数组 |
适用于 m * n <= 10^5
的约束条件。
📌 示例详细解析
示例:grid = [[2, 4], [6, 8]]
, x = 2
- 展平为
[2, 4, 6, 8]
- 基准数
base = 2
- 检查
(num - base) % x
是否为 0:(4-2)%2 == 0
,(6-2)%2 == 0
,(8-2)%2 == 0
✅
- 转换为步数(操作次数):
[(2-2)//2, (4-2)//2, (6-2)//2, (8-2)//2]
=[0, 1, 2, 3]
- 中位数是
1
或2
- 操作数总和 =
abs(0-2)+abs(1-2)+abs(2-2)+abs(3-2)
=2+1+0+1 = 4
⚖️ 方法分析与比较
方法 | 说明 |
---|---|
枚举法 | 尝试所有可能目标数(代价高,不推荐) |
本文方法 | 利用中位数最小化总操作数,时间效率高,空间也在允许范围内 ✅ |
⚠️ 注意:这个题目不能用平均数作为目标值,因为平均数不能保证最小化绝对差值之和,且结果可能不是整数。
✅ 总结
这道题结合了数学推理与贪心策略:
- 通过差值模
x
判断是否有解; - 通过排序与中位数选择,最小化操作次数;
- 时间与空间复杂度都在可控范围内,适合在面试或竞赛中使用。
🧪 拓展思考
- 如果只能加
x
,不能减呢?会变成单向操作优化问题。 - 如果每次可以选择不同的
x
值?会变成动态规划或图论问题。 - 如果还要记录操作路径?可以引入 BFS 或构建状态图。