【路飞】1658. 将 x 减到 0 的最小操作数

136 阅读2分钟

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;
}