📅  最后修改于: 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)$。
这个问题比较巧妙,需要我们转化一下问题的形式,但是如果我们将问题抽象到更高维度,就会发现这个问题变得简单明了了。这是一个非常好的思维方式,应用广泛,值得掌握。