题目描述

✅ 1526. 形成目标数组的子数组最少增加次数

image-20260929084856810

image-20260929084857087

题意分析

初始数组与 target 等长,全部为零。一次操作可以选择任意非空连续区间,把其中每个位置都加一,求最终恰好得到 target 的最少操作次数。

区间可以重叠,但只允许增加,不能先超过目标再减少。题目只要最少次数,不要求真的输出这些区间,也不需要模拟每一次加一。

解法:贪心累加正向增量

核心思路

[!blue]

把每次操作看作一个覆盖若干相邻位置的区间。每个位置最终值,就是覆盖它的操作区间数量。因此问题转化为:用尽量少的区间,让每个位置被覆盖指定次数。

第一个位置需要 target[0] 次覆盖,这些操作无法从左侧延续过来,至少需要从这里开始这么多个区间。向右处理时,若当前目标比左侧高出 diff,延续过来的区间最多只有左侧覆盖数量,差额至少需要 diff 个新开始的区间。

所以“首项 + 所有相邻正差”是任何方案都无法低于的操作数下界。下降并不产生新的下界,因为可以让多余区间在前一项结束。

这个下界也能达到:维持当前覆盖的区间数量等于目标高度,上升时新开差额数量的区间,持平时全部延续,下降时结束多余区间。每次新开对应一次实际操作,延续或结束不会新增操作,且每个位置的覆盖数恰好正确。

因为同一个公式既是下界,又有明确构造能够达到,它就是最少次数。只需累加这些新开数量,无需记录具体区间。

解题步骤

  1. 将答案初始化为首项 target[0]。
  2. 从第二项开始计算当前值减去前一项的差。
  3. 差为正时累加到答案,差非正时不增加。
  4. 扫描完返回总数,输入数组无需修改。

代码实现

class Solution {
    public int minNumberOperations(int[] target) {
        // 首项相对初始零高度,需要这些区间覆盖。
        int answer = target[0];

        for (int i = 1; i < target.length; i++) {
            int diff = target[i] - target[i - 1];

            // 正增量至少需要新开相应数量的区间,下降只需结束旧区间。
            if (diff > 0) {
                answer += diff;
            }
        }

        return answer;
    }
}
func minNumberOperations(target []int) int {
    // 首项相对初始零高度,需要这些区间覆盖。
    answer := target[0]
    for i := 1; i < len(target); i++ {
        diff := target[i] - target[i-1]
        // 正增量至少需要新开相应数量的区间,下降只需结束旧区间。
        if diff > 0 {
            answer += diff
        }
    }
    return answer
}

复杂度分析

  • 时间复杂度:$O(n)$,每对相邻目标值检查一次。
  • 空间复杂度:$O(1)$,只保存答案和当前差值。

关键点总结

[!green]

  • 操作区间数量对应覆盖层数,正差要求新开区间。
  • 下界来自必须补足的覆盖缺口,延续与结束区间的构造保证可达。
  • 下降只是结束已有操作的作用范围,不需要额外花费。

易错点总结

[!yellow]

  • 对相邻差取绝对值,会把下降也算作新操作,重复收费。
  • 只看全局最大值,无法反映低谷隔开的多个高段需要分别新开区间。
  • 忘记首项会漏掉从零升到第一处高度的全部覆盖。
  • 逐次模拟加一会按目标数值重复工作,而需要统计的只是新开始的次数。

相似题目

题目 难度 关联与区别
370. 区间加法 中等 原题给区间增加操作求结果,本题给最终数组求最少操作,差分中的正增量就是必须新开的操作数。
3229. 使数组等于目标数组所需的最少操作次数 困难 原题从一般初始数组出发且允许整段增减,本题从全0出发只允许增加,是更简单的差分情形。
转载与许可
作者
链接 https://hgnulb.github.io/blog/2026/33418239
许可 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!