【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 <= nums[i] <= 10^4

1 <= x <= 10^9

算法原理

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

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

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

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

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

由于题目中的数组元素都是在 1 <= nums[i] <= 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;
    }
};

两种写法都是可以通过的


完!

相关推荐
flashlight_hi2 小时前
LeetCode 分类刷题:203. 移除链表元素
算法·leetcode·链表
py有趣2 小时前
LeetCode算法学习之数组中的第K个最大元素
学习·算法·leetcode
what_20182 小时前
list集合使用
数据结构·算法·list
hetao17338373 小时前
2025-11-13~14 hetao1733837的刷题记录
c++·算法
hansang_IR3 小时前
【题解】洛谷 P2476 [SCOI2008] 着色方案 [记搜]
c++·算法·记忆化搜索
趙卋傑3 小时前
常见排序算法
java·算法·排序算法
阿巴~阿巴~4 小时前
IPv4地址转换函数详解及C++容器安全删除操作指南
linux·服务器·c++·网络协议·算法·c++容器安全删除操作·ipv4地址转换函数
TT哇4 小时前
【面经 每日一题】面试题16.25.LRU缓存(medium)
java·算法·缓存·面试
SKYDROID云卓小助手4 小时前
无人设备遥控器之差分信号抗干扰技术
网络·stm32·单片机·嵌入式硬件·算法