1187. 使数组严格递增
考点
- LIS
- 贪心
思路
题意
给定数组 \(A=\texttt{arr1}\),\(B=\texttt{arr2}\)。
允许对 \(A\) 的任意位置执行替换操作:将 \(A[i]\) 替换为 \(B\) 中任意元素(每次替换计 1 次)。
目标是使最终数组严格递增: \[ A[0] < A[1] < \cdots < A[n-1] \] 求最少替换次数;若无解,返回 \(-1\)。
两种设计思路总览
设计一 显式维护“当前前缀最终数组的最后一个值 last”,并记录达到该 last 所需的最小替换次数。
设计二 不直接构造最终数组,而是枚举哪些位置不被替换(称为保留点)。 在相邻保留点之间,用 \(B\) 中的元素整体替换填充,只要该区间可以被合法填满即可。 最大化保留点数量,答案即为 \(n-\text{保留点数}\)。
结构示意
设计一(构造式)
1 | 逐位扫描 A[i]: |
设计二(骨架式)
1 | 在 A 上选若干“保留点”(不替换): |
设计一:维护 last → 最小替换次数(前沿 DP)
1. 状态定义
定义: \[ f[i][last] \] 表示处理完前缀 \(A[0..i-1]\) 后,最终数组末尾值为 \(last\) 时的最小替换次数。
初始状态:
- 前缀为空
- 设 \(last=-\infty\)
- 替换次数为 0
2. 转移规则
处理位置 \(i\),其原始值为 \(A[i]\)。
从每个状态 \((last, cost)\) 出发,有两种选择。
2.1 不替换
若 \[ A[i] > last \] 则可以保留 \(A[i]\),转移为: \[ f[i+1][A[i]] = \min\bigl(f[i+1][A[i]],\ cost\bigr) \]
2.2 替换(只取刚好大于 last 的最小值)
若选择替换,则需要从 \(B\) 中选一个值 \(x\),满足: \[ x > last \] 替换一次,代价增加 1。
注意:在固定 last 的情况下,所有替换操作的代价都是一样的(都是 cost+1),因此此时真正影响后续可行性的,只有“替换后的末尾值大小”。
为了给未来留下最大的选择空间,应当选择最小的可行替换值: \[ x = \min\{b \in B \mid b > last\} \] 转移为: \[ f[i+1][x] = \min\bigl(f[i+1][x],\ cost+1\bigr) \]
2.3 为什么替换只取最小可行值
设存在两个可行替换值: \[ last < x_1 < x_2 \] 二者的替换代价相同。
但后续必须满足“下一项大于末尾值”,于是: \[ \{y \mid y > x_2\} \subset \{y \mid y > x_1\} \] 选择更大的 \(x_2\) 只会减少后续可行选择空间,而不会降低替换次数。
因此,\(x_2\) 被 \(x_1\) 严格支配,只保留最小的可行替换值即可。
3. AC代码
1 | class Solution { |
设计二:枚举保留点 + 区间填空判定
1. 从“最少替换”转为“最多保留”
若最终保留 \(K\) 个位置不替换,则: \[ \text{替换次数} = n - K \] 因此问题转化为:最大化保留点数量。
2. 两个保留点之间的可行性判定
设相邻两个保留点分别为 \(j < i\),其值为 \(A[j]\)、\(A[i]\)
中间需要替换的元素数量为: \[ t = i - j - 1 \] 这些被替换的值必须满足: \[ A[j] < x_1 < x_2 < \cdots < x_t < A[i] \quad,\quad x_k \in B \] 将 \(B\) 排序并去重为: \[ b[0], b[1], \ldots, b[m-1] \] 令: \[ k = \min\{p \mid b[p] \ge A[i]\} \] 即 \(b[0..k-1]\) 都严格小于 \(A[i]\)。
为了尽量降低下界压力,应选择最靠近 \(A[i]\) 的 \(t\) 个数: \[ b[k-t], b[k-t+1], \ldots, b[k-1] \] 因此,区间可被填满的充要条件是:
- 数量足够:\(k \ge t\)
- 最小的替换值仍大于左端点:
\[ b[k-t] > A[j] \]
3. 哨兵的作用
在数组末尾追加一个 \(+\infty\)
这样最后一个位置必然可以作为保留点,避免对结尾位置单独分类讨论。
4. DP 定义与转移
按“前缀长度”定义状态: \[ f[i] = \text{在前 } i \text{ 个元素中,以 } A[i-1] \text{ 作为保留点结尾的最大保留点数} \] 转移时枚举上一保留点位置 \(j\),中间替换数为: \[ t = i - j - 1 \]
- 若 \(j = 0\):表示左侧没有保留点,只要 \(k \ge i-1\) 即可
- 若 \(t = 0\):相邻保留点,只需 \(A[j-1] < A[i-1]\)
- 若 \(t \ge 1\):需 \(A[j-1] < A[i-1]\) 且 \(b[k-t] > A[j-1]\)
满足即: \[ f[i] = \max(f[i], f[j] + 1) \] 最终答案为: \[ (n-1) - (f[n] - 1) = n - f[n] \]
5. AC代码
1 | class Solution { |
思考题(变形)
问题
允许将 \(A[i]\) 替换为任意整数,求最少替换次数使数组严格递增。
推导
严格递增意味着相邻至少增加 1,因此对任意 \(i > j\): \[ A[i] \ge A[j] + (i - j) \] 等价于: \[ A[i] - i \ge A[j] - j \] 定义新数组: \[ B[i] = A[i] - i \] 则原问题等价于:使 \(B\) 非递减。
最大可保留位置数量等于 \(B\) 的最长非递减子序列长度,记为 LNDS;
最少替换次数为: \[ n - \text{LNDS} \]
样例说明
给定: \[ A = [1,2,2,3] \] 其最长严格递增子序列长度为 3,但要使整个数组严格递增,至少需要替换 2 个数。
原因在于严格递增不仅要求数值递增,还隐含了“下标间隔必须对应至少 1 的增量”这一约束。
通过变换 \(B[i]=A[i]-i\),该约束被吸收进数值中,从而转化为普通的非递减判定,LNDS 才能给出正确答案。