[每日一题]1144. 递减元素使数组呈锯齿状

1144. 递减元素使数组呈锯齿状 给你一个整数数组 nums,每次 操作 会从中选择一个元素并 将该元素的值减少 1。

如果符合下列情况之一,则数组 A 就是 锯齿数组:

  • 每个偶数索引对应的元素都大于相邻的元素,即 A[0] > A[1] < A[2] > A[3] < A[4] > …
  • 或者,每个奇数索引对应的元素都大于相邻的元素,即 A[0] < A[1] > A[2] < A[3] > A[4] < …

返回将数组 nums 转换为锯齿数组所需的最小操作次数。

输入:nums = [1,2,3] 输出:2 解释:我们可以把 2 递减到 0,或把 3 递减到 1。

输入:nums = [9,6,1,6,2] 输出:4

提示:

  • 1 <= nums.length <= 1000
  • 1 <= nums[i] <= 1000

Solution

class Solution {
    public int movesToMakeZigzag(int[] nums) {
        int ans = (int) 1e9, n = nums.length;
        int sum = 0;
        for(int i=1; i<n;i+=2){
            int min =  nums[i-1];
            if(i<n-1){
                min = Math.min(min, nums[i+1]);
            }
            if(min<=nums[i]){
                sum += nums[i]- min+1;
            }
        }
        ans = sum;
        sum = 0;
        for(int i=1;i<n;i+=2){
            if(nums[i-1]>=nums[i]){
                sum += nums[i-1] - nums[i] + 1;
            }
            if(i<n-1 && nums[i+1] >=nums[i]){
                sum += nums[i+1] - nums[i] + 1;
                nums[i+1] = nums[i]-1;
            }
        }
        ans = Math.min(ans, sum);
        return ans;
    }
}

Ideas

题目给出一个长度为 n 的整数数组 nums,每次操作会从中选择一个元素并将该元素减少 1。现在给出「锯齿数组」的定义,若一个数组 A 符合下列情况之一,则它就是「锯齿数组」:

  • 每个偶数索引对应的元素都大于相邻的元素,即 A[0] > A[1] < A[2] > A[3] < A[4] > …
  • 或者,每个奇数索引对应的元素都大于相邻的元素,即 A[0] < A[1] > A[2] < A[3] > A[4] < …

现在我们需要求得将 nums 转换为「锯齿数组」所需的最小操作次数。不失一般性,我们设我们最后求得的「锯齿数组」满足每一个偶数索引对应的元素都大于其相邻的元素。

因为操作的先后并不会影响最终结果,所以我们若我们要对某些偶数下标的元素进行操作,则先完成该些操作,然后再统一对奇数下标的元素进行操作,设数组 p 为对 nums 某些偶数下标的元素进行操作后的数组,那么为了使数组 p 为满足每一个偶数索引对应的元素都大于其相邻的元素的「锯齿数组」,其奇数下标的元素都需要小于其相邻元素的最小值,即为了使某一个奇数下标位置 i 满足要求的最少操作次数 ci=max(p[i]−q(i)+1,0),其中 q(i) 表示数组 p 中位置 i 相邻元素的最小值,因为若我们对某个偶数下标的元素进行了操作,则该元素相邻的奇数下标元素所需要的操作次数只增不减,但是总的操作次数一定增加了,所以最优解中一定不会存在对偶数下标操作的情况。

那么我们对 nums 中每一个奇数位置 i 的 c i求和即为此时求每一个偶数索引对应的元素都大于其相邻的元素的「锯齿数组」的最少操作的次数。对于求每一个奇数索引对应的元素都大于其相邻的元素的「锯齿数组」的最小操作次数同理,最终返回两者的较小值即可。