题目地址:
https://leetcode.com/problems/minimum-number-of-increments-on-subarrays-to-form-a-target-array/
给定一个长 n n n的目标数组 A A A,并且给定一个全是 0 0 0的长 n n n的数组作为初始数组,对初始数组可以进行这样的操作,每次选取一个子区间,将这个区间内的数都加 1 1 1。问要变成 A A A至少需要多少步。
其实答案等于 A [ 0 ] + ∑ i ≥ 1 max { 0 , A [ i ] − A [ i − 1 ] } A[0]+\sum_{i\ge 1} \max\{0,A[i]-A[i-1]\} A[0]+∑i≥1max{0,A[i]−A[i−1]},因为对于连续下降的子段,代价就是那个最大值,而如果有上升,这个上升的差必须额外耗费操作。严格证明如下:
数学归纳法。如果 n = 1 n=1 n=1显然。假设对 n − 1 n-1 n−1成立,当数组长 n n n的时候,如果 A [ n ] ≤ A [ n − 1 ] A[n]\le A[n-1] A[n]≤A[n−1],根据归纳假设,存在上述代价的操作将 A [ 0 : n − 1 ] A[0:n-1] A[0:n−1]变成 0 0 0,那么由于这个操作也把 A [ n − 1 ] A[n-1] A[n−1]变成 0 0 0了,我们可以凡是含 A [ n − 1 ] A[n-1] A[n−1]的操作都延伸到 A [ n ] A[n] A[n]恰好 A [ n ] A[n] A[n]次,这样顺便把 A [ n ] A[n] A[n]也变成 0 0 0了而操作次数不变,从而成立;如果 A [ n ] > A [ n − 1 ] A[n]> A[n-1] A[n]>A[n−1],那么任何把 A A A变成 0 0 0数组的操作都会某一个时刻将 A [ n − 1 ] A[n-1] A[n−1]变成 0 0 0,并且最优方案一定是顺便把 A [ n ] A[n] A[n]也减少 A [ n − 1 ] A[n-1] A[n−1]这么多,我们调整操作次序,把单独对 A [ n : n ] A[n:n] A[n:n]的操作挪到最后,由归纳假设要将 A [ 0 : n − 1 ] A[0:n-1] A[0:n−1]变成 0 0 0的方案的最少次数是上述值,那么还需要 A [ n ] − A [ n − 1 ] A[n]-A[n-1] A[n]−A[n−1]步再把 A [ n ] A[n] A[n]变成 0 0 0,从而结论也成立。
代码如下:
class Solution {
public:
int minNumberOperations(vector<int>& target) {
int res = target[0];
for (int i = 1; i < target.size(); i++)
if (target[i] > target[i - 1]) res += target[i] - target[i - 1];
return res;
}
};
时间复杂度 O ( n ) O(n) O(n),空间 O ( 1 ) O(1) O(1)。