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

两种写法都是可以通过的


完!

相关推荐
LYFlied17 小时前
【每日算法】LeetCode 1143. 最长公共子序列
前端·算法·leetcode·职场和发展·动态规划
长安er18 小时前
LeetCode 20/155/394/739/84/42/单调栈核心原理与经典题型全解析
数据结构·算法·leetcode·动态规划·
MarkHD19 小时前
智能体在车联网中的应用:第28天 深度强化学习实战:从原理到实现——掌握近端策略优化(PPO)算法
算法
能源系统预测和优化研究19 小时前
【原创代码改进】考虑共享储能接入的工业园区多类型负荷需求响应经济运行研究
大数据·算法
yoke菜籽19 小时前
LeetCode——三指针
算法·leetcode·职场和发展
小高不明20 小时前
前缀和一维/二维-复习篇
开发语言·算法
bin915320 小时前
当AI优化搜索引擎算法:Go初级开发者的创意突围实战指南
人工智能·算法·搜索引擎·工具·ai工具
曹牧21 小时前
Java:Math.abs()‌
java·开发语言·算法
CoovallyAIHub1 天前
纯视觉的终结?顶会趋势:不会联觉(多模态)的CV不是好AI
深度学习·算法·计算机视觉
CoovallyAIHub1 天前
一文读懂大语言模型家族:LLM、MLLM、LMM、VLM核心概念全解析
深度学习·算法·计算机视觉