每日一题——将 x 减到 0 的最小操作数

146 阅读1分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 1 天,点击查看活动详情


1658. 将 x 减到 0 的最小操作数

给你一个整数数组 nums 和一个整数 x 。每一次操作时,你应当移除数组 nums 最左边或最右边的元素,然后从 x 中减去该元素的值。请注意,需要 修改 数组以供接下来的操作使用。

如果可以将 x 恰好 减到 0 ,返回 最小操作数 ;否则,返回 -1 。

 

示例 1:

输入: nums = [1,1,4,2,3], x = 5
输出: 2
解释: 最佳解决方案是移除后两个元素,将 x 减到 0 。

示例 2:

输入: nums = [3,2,20,1,1,3], x = 10
输出: 5
解释: 最佳解决方案是移除后三个元素和前两个元素(总共 5 次操作),将 x 减到 0 。

 

提示:

  • 1 <= nums.length <= 105
  • 1 <= nums[i] <= 104
  • 1 <= x <= 109

思路

看到这个题的第一思路是分别判断数组左右两端的值,尽可能地移除最大的值,但这种贪心的方法会导致局部最优解,是没办法解决这个问题的.

然后想到使用动态规划,分别记录数组两端移除的数组值,但是这种方法感觉很麻烦,也不可行。

其实这个题需要转换一下思路,将数组两端的值移除 x,就是将数组中间部分的值确定为 sum - x,这样只需要关注数组中间部分即可.

可以通过滑动窗口的方法来遍历数组中间部分的值,累加窗口右边界的值,如果超过了目标值,则左边界右移,减少中间和,以确保中间和小于等于目标和,依次右滑窗口遍历所有中间和,取出符合目标和的最长长度,即可得到两端减少的最小次数。

题解

class Solution {
    public int minOperations(int[] nums, int x) {
        long sum = 0;
        for(int num: nums) {
            sum += num;
        }
        int ans = Integer.MAX_VALUE;
        int length = nums.length;
        long target = sum - x;
        if(target < 0) {
            return -1;
        }
        long tmp = 0;
        for(int left = 0, right = 0; right < length; right++) {
            tmp += nums[right];
            if(tmp == target) {
                ans = Math.min(ans, length - (right - left + 1));
            }
            while(tmp > target && left < length) {
                tmp -= nums[left++];
            }
            if(tmp == target) {
                ans = Math.min(ans, length - (right - left + 1));
            }
        }
        return ans == Integer.MAX_VALUE? -1: ans;
    }
}