【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,就更新一下结果。
代码如下:
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 的条件判断的,写法不唯一。
如果是要单独判断的话,代码是下面这种:
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;elsereturn nums.size() - ret;}
};
两种写法都是可以通过的

完!
