最小操作次数使数组严格递增:基于下降点的贪心算法详解

3次阅读

最小操作次数使数组严格递增:基于下降点的贪心算法详解

本文介绍一种时间复杂度为 o(n) 的高效贪心算法,通过识别并统计“破坏单调性的下降位置”来求解将数组变为严格递增所需的最少段增操作次数,而非累加数值差。

本文介绍一种时间复杂度为 o(n) 的高效贪心算法,通过识别并统计“破坏单调性的下降位置”来求解将数组变为严格递增所需的最少段增操作次数,而非累加数值差。

在解决“使数组严格递增所需的最少段增操作数”问题时,一个常见误区是将操作次数与需填补的数值差(如 max_so_far + 1 – A[i])混淆。实际上,每次操作允许对任意连续子数组的所有元素同时增加任一正整数——这意味着一次操作可修复后续多个不满足 A[i]

核心洞察:操作次数 = 下降点数量

观察严格递增的定义:对所有 i ∈ [0, n-2],必须满足 A[i] = A[i+1],则称位置 i 是一个下降点(descent point)
重要性质:

  • 每次对 [j, k](j ≤ i
  • 无法修复位于 i (因左侧已固定);
  • 更关键的是:修复某个下降点 i 的最简方式,是从此处向右延伸一次操作——因为后续所有元素均可被同步抬高,从而避免为每个不满足位置单独操作。

由此得出最优策略:

从左到右扫描,每当遇到 A[i] >= A[i+1],就执行一次操作覆盖 i+1 及其右侧全部元素;操作次数即为下降点总数。

这等价于统计满足 A[i] >= A[i+1] 的索引 i 的个数。

正确实现与代码

def solution(A):     if len(A) <= 1:         return 0     moves = 0     for i in range(len(A) - 1):         if A[i] >= A[i + 1]:             moves += 1     return moves

验证示例:

  • A = [4, 2, 4, 1, 3, 5]:
    比较 (4,2)→≥ ✅, (2,4)→2 个下降点 → 返回 2 ✔️
  • A = [3, 5, 7, 7]:
    (7,7)→≥ ✅ → 1 个下降点 → 返回 1 ✔️
  • A = [1, 5, 6, 10]:全 0 个下降点 → 返回 0 ✔️

为什么原代码错误?

原实现误将“段增操作”理解为“单点增量调整”,错误地累加了数值差:

diff = max_so_far + 1 - current_val moves += diff  # ❌ 错:diff 是增量值,不是操作次数!

例如 [2, 1] 中,diff = 2,但只需1 次操作(对 [1] 或 [2,1] 整体加 1 即可得 [2,2]→再加 1 得 [2,3],但最优是直接对第2个元素加1 → [2,2] 不合法,故对 [1] 加2 → [2,3],仍为1次操作)。无论加多少,一次段增就是1次操作

注意事项与边界说明

  • ✅ 时间复杂度:O(n),仅一次遍历,满足 N ≤ 10⁵ 要求;
  • ✅ 空间复杂度:O(1),无需额外数组;
  • ⚠️ 本解法不构造具体操作方案(如选哪些段、加多少),仅求最小次数——这正是题干所求;
  • ⚠️ 数值范围(如 1e9)不影响逻辑,因我们只做比较,不计算差值;
  • ✅ 支持任意正整数输入,包括重复值和大幅波动序列。

总结

该问题的本质是利用段增操作的“批量修正”能力,将修复成本解耦于数值大小,转而聚焦于结构缺陷的位置数量。只要识别出所有相邻逆序对(即 A[i] >= A[i+1]),其总数即为理论下界,且可通过贪心策略(每次从下降点右侧起始覆盖剩余部分)达到。这一思路简洁、高效、鲁棒,是典型“重定义问题维度”以突破直觉陷阱的算法范例。

text=ZqhQzanResources