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 。
解题思路:这个的解题思路是利用前缀和,一个是两个前缀和一个从左开始,一个从右开始,然后遍历左边前缀和数组,从右前缀和里找有没有复核x减当前做前缀和值的值,代码如下
两个前缀和解法
var minOperations = function(nums, x) {
//从左边开始的前缀和
let dataL = [0];
//右边开始的前缀和
let dataR = [0];
//统计左边前缀和
let sum = 0;
//获取当前数组长度
let j = nums.length - 1;
//统计右边数组
let sum2 = 0;
for(let i = 0; i < nums.length; i ++){
sum += nums[i];
dataL.push(sum);
sum2 += nums[j];
dataR.push(sum2)
j --;
}
//记录最少操作数量
let ansAll = -1;
for(let i = 0; i < dataL.length; i ++){
//当前值等于x的时候如果ansAll还没赋值就需要给ansAll赋值,
if(dataL[i] == x && ansAll == -1) ansAll = i;
else if(dataL[i] == x) ansAll = ansAll < i ? ansAll : i;//当ansAll存在就选择小的赋值
//当前值如果大于x肯定右前缀和肯定找不到了
if(dataL[i] >= x) continue;
//右前缀和查找x-dataL[i]
let j = min_operations(dataR,x - dataL[i]);
//等于-1说明找不到
if(j == -1) continue;
//ansAll == -1说是第一个赋值,((i + j) < dataL.length) && i + j < ansAll找到比当前更少的操作数,(i + j) < dataL.length如果操作数大于当前数组的长度,说明有交叉,不符合要求
if(ansAll == -1 || ((i + j) < dataL.length) && i + j < ansAll ) ansAll = i + j;
}
//因为会存在第一次赋值存在i+j大于 dataL.length,因为是不符合的操作数量,所以取-1,
return ansAll > dataL.length - 1 ? -1 : ansAll;
};
let min_operations = function(data,x){
let head = 0, tail = data.length - 1,m;
while(head <= tail){
m = (head + tail) >> 1;
if(data[m] == x) return m;
if(data[m] < x) head = m + 1;
else tail = m - 1;
}
return -1;
}
一个前缀和解法
var minOperations = function(nums, x) {
//右边前缀和
let dataR = [0];
//统计右边前缀和
let sum = 0;
//求从右边开始的前缀和
for(let i = nums.length - 1; i >= 0; i --){
sum += nums[i];
dataR.push(sum)
}
//记录操作数量
let anmAll = -1;
//记录左边开始的前缀和
let sum2 = 0;
for(let i = 0; i < nums.length; i ++){
//当前值等于x的时候如果ansAll还没赋值就需要给ansAll赋值,
if(sum2 == x && anmAll == -1) anmAll = i;
else if(sum2 == x) anmAll = anmAll < i ? anmAll : i;//当ansAll存在就选择小的赋值
//当前值如果大于x肯定右前缀和肯定找不到了
if(sum2 >= x) continue;
let j = min_operations(dataR,x - sum2);
//因为存在右边前缀和第一个就是x,所以sum2从0开始对比,前缀和第一个默认就是0,
sum2 += nums[i];
if(j == -1) continue;
if(anmAll == -1 || (i + j < nums.length && i + j < anmAll)) anmAll = i + j;
}
return anmAll > nums.length ? -1 : anmAll;
};
let min_operations = function(data,x){
let head = 0, tail = data.length - 1,m;
while(head <= tail){
m = (head + tail) >> 1;
if(data[m] == x) return m;
if(data[m] < x) head = m + 1;
else tail = m - 1;
}
return -1;
}