【LeetCode】将 x 减到 0 的最小操作数

文章目录


前言

本文探讨了如何找到将数组两端元素之和减到目标值x的最小操作数。通过逆向思维,将问题转化为在数组中间寻找最长子数组,使其和等于总和减x。利用滑动窗口算法高效解决该问题,时间复杂度为O(n)!


题目描述

给你一个整数数组 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 <= 10^5

1 <= numsi <= 10^4

1 <= x <= 10^9

算法原理

结合给出的示例,题目的意思不难理解,但如果直接按照题目的意思来做这道题,好像有种无从下手的感觉,因为情况太多了,不好处理。

这个时候有人给出了一种解法,非常的巧妙,那就是正难则反,我们可以换个角度来做这道题,那这道题会非常简单(当然这种方法肯定不是我想到的)

题目原来的意思我们可以理解为让我们在数组两边找到最少的元素,让它们的和等于 x,那也就相当于让我们在数组中间找最多的元素,让它们的和等于整个数组的和再减去 x,画图理解:

如图所示,我们让 a 和 b 区间的和等于 x,等价于让 c 区间的和等于数组所有元素的总和 sum 再减去 x。

根据题目给出的示例,我们发现数组的左右区间是可以为 0 的,所以现在的问题就变成了在数组左右元素中找到一个子数组,让子数组中的和等于一个目标值且数组长度最大,跟我们之前做过的长度最小的子数组那道题很像,那具体我们要这么做呢?

由于题目中的数组元素都是在 1 <= numsi <= 10^4 这个区间范围的,即都是大于 0 的,所以我们依然可以用滑动窗口来做这道题。

代码实现

定义两个指针 left 和 right。首先是进窗口,然后是循环的判断、出窗口,循环的过程中,如果区间的和等于目标值即数组元素总和减去 x,就更新一下结果。

代码如下:

cpp 复制代码
class Solution
{
public:
    int minOperations(vector<int>& nums, int x)
    {
        int n = nums.size(), left = 0, right = 0, sum = 0, len = -1, target = 0; 
        for (auto& e : nums)
        {
            target += e;
        }
        target = target - x;
        while (right < n)
        {
            sum += nums[right++];
            while (sum > target && left < right)
            {
                sum -= nums[left++];
            }
            if (sum == target)
            {
                len = max(len, right - left);
            }
        }
        if (len == -1) return -1;
        return n - len;
    }
};

这里有个特殊情况要注意一下,那就是我们的目标值是有可能会大于数组元素总和的,这种情况下我们直接返回 -1即可,咱们上边的代码是在循环条件那里加了一个 left < right 的条件判断的,写法不唯一。

如果是要单独判断的话,代码是下面这种:

cpp 复制代码
class Solution {
public:
    int minOperations(vector<int>& nums, int x) {
        int sum = 0;
        for (int a : nums)
            sum += a;
        int target = sum - x;
        // 细节问题
        if (target < 0)
            return -1;
        int ret = -1;
        for (int left = 0, right = 0, tmp = 0; right < nums.size(); right++) {
            tmp += nums[right];      // 进窗⼝
            while (tmp > target)     // 判断
                tmp -= nums[left++]; // 出窗⼝
            if (tmp == target)       // 更新结果
                ret = max(ret, right - left + 1);
        }
        if (ret == -1)
            return ret;
        else
            return nums.size() - ret;
    }
};

两种写法都是可以通过的


完!

相关推荐
闪电悠米5 小时前
力扣hot100-240.搜索二维矩阵2-单调性剪枝详解
数据结构·算法·leetcode·矩阵·哈希算法
imuliuliang5 小时前
关于基于图论的最短路径算法性能对比分析7
算法
imuliuliang5 小时前
关于从栈与队列看算法思维的演化路径7
算法
春生野草6 小时前
个人笔记--大顶堆和基数排序
java·数据结构·算法
白狐_7986 小时前
【408计算机网络|第04章·下|408-CN-04B】网络层(下):路由算法、RIP、OSPF、BGP、IPv6与路由器
计算机网络·算法·智能路由器
donoot7 小时前
PaddleOCR + PyMuPDF 生成【全兼容双层 PDF】完整实操指南
人工智能·算法·pymupdf·paddleocr·双层pdf
paeamecium8 小时前
【PAT甲级真题】- Rational Sum (20)
数据结构·c++·python·算法·pat考试·pat
拳里剑气17 小时前
C++算法:BFS解决FloodFill算法
c++·算法·bfs·宽度优先
wanderist.19 小时前
Lambda表达式在算法竞赛中的应用
java·开发语言·算法