开启掘金成长之旅!这是我参与「掘金日新计划 · 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 <= 1051 <= nums[i] <= 1041 <= 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;
}
}