「这是我参与2022首次更文挑战的第21天,活动详情查看:2022首次更文挑战」
1658. 将 x 减到 0 的最小操作数
给你一个整数数组 nums 和一个整数 x 。每一次操作时,你应当移除数组 nums 最左边或最右边的元素,然后从 x 中减去该元素的值。请注意,需要 修改 数组以供接下来的操作使用。
如果可以将 x 恰好 减到 0 ,返回 最小操作数 ;否则,返回 -1 。
示例1:
输入: nums = [1,1,4,2,3], x = 5
输出: 2
解释: 最佳解决方案是移除后两个元素,将 x 减到 0 。
示例2:
输入: nums = [5,6,7,8,9], x = 4
输出: -1
示例3:
输入:nums = [3,2,20,1,1,3], x = 10
输出:5
解释:最佳解决方案是移除后三个元素和前两个元素(总共 5 次操作),将 x 减到 0 。
提示:
1 <= nums.length <= 1051 <= nums[i] <= 1041 <= x <= 109
前缀和后缀和+遍历
思路
有题意可知,我们需要从数组的前面或者后面依次选择元素,将选择的元素相加正好等于目标值x
这里我们用前缀和和后缀和来记录对应的值,这样我们就可以直接用前缀和第p位和后缀和第q位相加来判断是否等于目标值x了
具体实现:
- 首先求前缀和before和后缀和after,如果有首位或者末位直接返回1即可
- 声明p和q分别指向前缀和第一位和后缀和第len-1位,while循环条件为p,q有效,且p < q
- 如果当前总和total小于x,那么q--
- 如果当前总和total等于x,那么给min赋值,如果为-1则直接赋值,否则与min比较去最小值。此时已经等于x了,没必要继续q--了,所以p++一次,并且while循环条件total>x,那么q++
- 如果当前总和total大于x,那么p++一次,并且while循环条件total>x,那么q++
最后返回min即可
var minOperations = function (nums, x) {
var min = -1;
var len = nums.length;
var before = Array(len).fill(0);
var after = Array(len).fill(0);
// 设置初始值,并判断是否符合答案
before[0] = nums[0];
after[len - 1] = nums[len - 1];
if (nums[0] === x || nums[len - 1] === x) return 1;
// 前缀和、后缀和 并记录是否有单边满足条件的答案
for (var i = 1; i < len; i++) {
before[i] = before[i - 1] + nums[i];
after[len - 1 - i] = after[len - i] + nums[len - 1 - i];
if (before[i] === x || after[len - 1 - i] === x) {
if (min === -1) {
min = i + 1;
} else {
min = Math.min(i + 1, min);
}
}
if (before[i] >= x && after[len - 1 - i] >= x) {
break;
}
}
// 特殊情况
// [1,1,2,3,5,6,7,2,2,3] 7 4+0 、 0+3、2+2、3+1
// 这里min最少>=2
var p = 0,
q = len - 1;
while (p >= 0 && q <= len - 1 && p < q) {
var total = before[p] + after[q];
if (total < x) {
q--;
}
if (total > x) {
p++
while (before[p] + after[q] > x && q < len) {
q++
}
}
if (total === x) {
if (min === -1) {
min = p + 1 + len - q;
} else {
min = Math.min(p + 1 + len - q, min);
}
p++
while (before[p] + after[q] > x && q < len) {
q++
}
}
}
return min;
};