
本文介绍一种时间复杂度为 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]),其总数即为理论下界,且可通过贪心策略(每次从下降点右侧起始覆盖剩余部分)达到。这一思路简洁、高效、鲁棒,是典型“重定义问题维度”以突破直觉陷阱的算法范例。









