目录
1749. 任意子数组和的绝对值的最大值
思路解析
算法1--动态规划
拿到这道题目,我们最朴素的想法就是枚举所有区间。但是这样的代价是O() ,面对 题目的数据量这样的代价是高昂的。所以,我们需要另寻他法。
首先这是一道 区间最优化题目。但从关键字 “区间” 而言,我们有 前缀和数组、树状数组、 线段树以及区间动规。
如果使用树状数组,那么在多次访问区间是较为低效的。
如果使用线段树(分治思想),那么我们为了保证连续区间的特点需要对跨区情况需要花费O(n)的时间去维护最大绝对值。最后我们只需要访问整个区间即可,也就是最后复杂度会来到O(nlogn)。当然,如果你是采用记忆化搜索的,时间复杂度可以来到O(n)。
关键在于最大绝对值的维护,我们的第一个想法就是某一段区间内的和最大或者最小。那么题目就转换成最大子序列和 与 最小子序列和。借此,你能知道为什么线段树可以采用记忆化了。如果你敢兴趣,您可以移步到我的另一篇blog:经典题:最大子序列和_诶咦的博客-优快云博客
AC代码:
class Solution {
struct Status {
int min, max;
};
public:
int maxAbsoluteSum(vector<int>& nums) {
int n_nums = nums.size();
vector<Status> dp(n_nums);
dp[0].min = nums[0], dp[0].max = nums[0];
for (int i = 1; i < n_nums; ++i) {
dp[i].min = min(dp[i - 1].min + nums[i], nums[i]);
dp[i].max = max(dp[i - 1].max + nums[i], nums[i]);
}
int ans = abs(nums[0]);
for (int i = 1; i < n_nums; ++i) {
ans = max(ans, max(abs(dp[i].min), abs(dp[i].max)));
}
return ans;
}
};
当然你也可以使用 “线段树(分治思想)” 作为一种基底编写代码。
算法2--前缀和
还记得我们面对 “区间” 关键字提出的做法吗?本质上来讲,树状数据也是一种前缀和数组且是线段树的优化。但很可惜,在多次访问这方面它不如原始的前缀和数组简单快捷。而区间和,例如区间 [l, r] 之间的元素和可以表示为 pre_sum[r] - pre_sum[l - 1]。
表达式就变成了 abs(pre_sum[r] - pre_sum[l - 1]) 的最大值,那么我们知道只要 pre_sum[r] - pre_sum[l - 1] 的差值最大即可,也就是我们只要去寻找 pre_sum 的最大最小值即可。
AC代码:
class Solution {
public:
int maxAbsoluteSum(vector<int>& nums) {
int n_nums = nums.size();
vector<int> pre_sum(n_nums + 1);
pre_sum[0] = 0;
for (int i = 1; i <= n_nums; ++i) {
pre_sum[i] = pre_sum[i - 1] + nums[i - 1];
}
int max = 0, min = 0;
for (int i = 1; i <= n_nums; ++i) {
if (max < pre_sum[i]) max = pre_sum[i];
if (min > pre_sum[i]) min = pre_sum[i];
}
return abs(max - min);
}
};