力扣9.30
1749. 任意子数组和的绝对值的最大值
给你一个整数数组 nums
。一个子数组 [numsl, numsl+1, ..., numsr-1, numsr]
的 和的绝对值 为 abs(numsl + numsl+1 + ... + numsr-1 + numsr)
。
请你找出 nums
中 和的绝对值 最大的任意子数组(可能为空),并返回该 最大值 。
abs(x)
定义如下:
- 如果 x 是负整数,那么
abs(x) = -x
。 - 如果 x 是非负整数,那么
abs(x) = x
。
数据范围
1 <= nums.length <= 105
-104 <= nums[i] <= 104
分析
最大子数组和的变式,可以求处最大子数组和和最小子数组和,然后取绝对值的max
代码
typedef long long LL;
class Solution {
public:
const static int N = 1e5 + 5, INF = 0x3f3f3f3f;
int dp1[N], dp2[N];
int maxAbsoluteSum(vector<int>& nums) {
int n = nums.size();
int res = 0;
dp1[0] = -INF;
dp2[0] = INF;
for(int i = 1; i <= n; i ++ ) {
dp1[i] = max(nums[i - 1], dp1[i - 1] + nums[i - 1]);
dp2[i] = min(nums[i - 1], dp2[i - 1] + nums[i - 1]);
res = max(res, abs(dp1[i]));
res = max(res, abs(dp2[i]));
}
return res;
}
};
1191. K 次串联后最大子数组之和
给定一个整数数组 arr
和一个整数 k
,通过重复 k
次来修改数组。
例如,如果 arr = [1, 2] , k = 3
,那么修改后的数组将是 [1, 2, 1, 2, 1, 2]
。
返回修改后的数组中的最大的子数组之和。注意,子数组长度可以是 0
,在这种情况下它的总和也是 0
。
由于 结果可能会很大,需要返回的 109 + 7
的 模 。
数据范围
1 <= arr.length <= 105
1 <= k <= 105
-104 <= arr[i] <= 104
分析
分类讨论,两种情况,对于k=1的情况,直接在长度为n的数组上做一次子数组最大和,对于k>1的情况,可能会出现起点和终点不在同一个区间内,因此需要对数组进行复制,而对于中间是否需要加上一整段区间,只需要看数组的和是否为正数,若是正数,则一定可以将整段区间插入到起点和终点之间。
代码
typedef long long LL;
class Solution {
public:
const static LL N = 1e5 + 5, INF = 0x3f3f3f3f, mod = (LL)1e9 + 7;
LL res = 0;
LL dp[2 * N];
LL kConcatenationMaxSum(vector<int>& arr, int k) {
int n = arr.size();
LL sum = 0;
for(int i = 0; i < n; i ++ ) {
sum += arr[i];
sum %= mod;
}
dp[0] = 0;
for(int i = 1; i <= n * (k > 1 ? 2 : 1); i ++ ) {
dp[i] = max((LL)0, dp[i - 1] + (LL)arr[(i - 1) % n]);
if(sum > 0 && k >= 2) res = max(res, dp[i] + sum * (k - 2) % mod);
else res = max(res, dp[i]);
dp[i] %= mod;
res %= mod;
}
return res % mod;
}
};
918. 环形子数组的最大和
给定一个长度为 n
的环形整数数组 nums
,返回 nums
的非空 子数组 的最大可能和 。
环形数组 意味着数组的末端将会与开头相连呈环状。形式上, nums[i]
的下一个元素是 nums[(i + 1) % n]
, nums[i]
的前一个元素是 nums[(i - 1 + n) % n]
。
子数组 最多只能包含固定缓冲区 nums
中的每个元素一次。形式上,对于子数组 nums[i], nums[i + 1], ..., nums[j]
,不存在 i <= k1, k2 <= j 其中 k1 % n == k2 % n
。
数据范围
n == nums.length
1 <= n <= 3 * 104
-3 * 104 <= nums[i] <= 3 * 104
分析
令dp[i]
表示以i结尾的最大子数组和,同时用len[i]
记录长度,对于环形,我们可以开两倍数组将首位相接,然后跑一边最大子数组和并记录长度,若长度大于n
,则说明需要删去首部一些点,由于我们已经记录了子数组的长度,所以可以轻松找到这段子数组的开头,之后找到前缀和小于0的最小值mins
,将dp[i]
减去mins
,同时更新len
即可
代码
class Solution {
public:
const static int N = 3e4 + 5, INF = 0x3f3f3f3f;
int dp[2 * N];
int len[2 * N];
int maxSubarraySumCircular(vector<int>& nums) {
int n = nums.size();
int res = -INF;
dp[0] = -INF;
for(int i = 1; i <= 2 * n; i ++ ) {
if(dp[i - 1] + nums[(i - 1) % n] > nums[(i - 1) % n]) {
dp[i] = dp[i - 1] + nums[(i - 1) % n];
len[i] = len[i - 1] + 1;
} else {
dp[i] = nums[(i - 1) % n];
len[i] = 1;
}
if(len[i] > n) {
int t = len[i];
int last = i - t + 1;
while(i - last + 1 > n) {
dp[i] -= nums[(last - 1) % n];
last ++ ;
}
int minsum = 0, sum = 0;
int pos = last - 1;
for(int k = last; k <= i; k ++ ) {
sum += nums[(k - 1) % n];
if(minsum > sum) {
minsum = sum;
pos = k;
}
}
len[i] = i - pos;
dp[i] -= minsum;
}
res = max(res, dp[i]);
}
return res;
}
};