📜  删除正或负子数组后数组的最大和(1)

📅  最后修改于: 2023-12-03 15:22:43.819000             🧑  作者: Mango

删除正或负子数组后数组的最大和

问题描述

给定一个整数数组 nums,你需要删除一个子数组(可以为空),使得剩余元素的和的绝对值的最大值最小。请你返回这个最小的绝对值。

注意,删除的子数组可以是非连续的,但是不能改变元素的顺序。

解决方案
思路

首先我们需要明确问题的本质——删除正或负子数组,是很明显的贪心策略。那么问题就是如何找到这个贪心策略的正确性。

首先我们可以尝试着将问题转化成一个更简单的形式,即求剩余元素的和的最小值。

显然,如果将数组分成两半,分别求出左半部分的和 $left$ 和右半部分的和 $right$,那么答案一定为 $|left - right|$。这是一个相对简单的问题,我们只需要对其进行二分查找即可。

但问题来了,如何将原问题转化成这个问题呢?我们不妨考虑一下前缀和,如果用 $sum[i]$ 表示数组的前缀和,那么 $left = sum[rightIdx] - sum[leftIdx - 1]$, $right$ 同理。这样就把问题转化成了在一段区间内找到一个左端点和一个右端点,使得两个前缀和的差的绝对值最小。由于存在删除子数组的限制,我们需要给原来的数组加上一维,即用 $dp[i][j]$ 表示从 $i$ 到 $j$ 的子数组可以删掉的最小的元素和,那么 $dp[i][j] = min(dp[i][j - 1], dp[i + 1][j], abs(sum[j] - sum[i - 1])) $,其中 $dp[i][i] = a[i]$,$dp[i][i + 1] = min(a[i], a[i + 1])$。

代码实现
def minimumAbsDifference(self, nums: List[int]) -> List[List[int]]:
    nums.sort()
    min_abs_diff = float('inf')
    ans = []
    for i in range(1, len(nums)):
        if nums[i] - nums[i - 1] < min_abs_diff:
            min_abs_diff = nums[i] - nums[i - 1]
            ans = [[nums[i - 1], nums[i]]]
        elif nums[i] - nums[i - 1] == min_abs_diff:
            ans.append([nums[i - 1], nums[i]])
    return ans
class Solution {
public:
    vector<vector<int>> minimumAbsDifference(vector<int>& nums) {
        sort(nums.begin(), nums.end());
        int min_abs_diff = INT_MAX;
        vector<vector<int>> ans;
        for (int i = 1; i < nums.size(); ++i) {
            if (nums[i] - nums[i - 1] < min_abs_diff) {
                min_abs_diff = nums[i] - nums[i - 1];
                ans = {{nums[i - 1], nums[i]}};
            } else if (nums[i] - nums[i - 1] == min_abs_diff) {
                ans.push_back({nums[i - 1], nums[i]});
            }
        }
        return ans;
    }
};
复杂度分析

时间复杂度:$O(nlogn)$。(主要是排序所需的时间)

空间复杂度:$O(n)$。

总结

这个问题比较巧妙,需要我们转化一下问题的形式,但是如果我们将问题抽象到更高维度,就会发现这个问题变得简单明了了。这是一个非常好的思维方式,应用广泛,值得掌握。