1031. 两个非重叠子数组的最大和
难度:中等
题目:
给你一个整数数组 nums
和两个整数 firstLen
和 secondLen
,请你找出并返回两个非重叠 子数组 中元素的最大和 , 长度分别为 firstLen
和 secondLen
。
长度为 firstLen
的子数组可以出现在长为 secondLen
的子数组之前或之后,但二者必须是不重叠的。
子数组是数组的一个 连续 部分。
示例 1:
输入: nums = [0,6,5,2,2,5,1,9,4], firstLen = 1, secondLen = 2
输出: 20
解释: 子数组的一种选择中,[9] 长度为 1,[6,5] 长度为 2。
示例 2:
输入: nums = [3,8,1,3,2,1,8,9,0], firstLen = 3, secondLen = 2
输出: 29
解释: 子数组的一种选择中,[3,8,1] 长度为 3,[8,9] 长度为 2。
示例 3:
输入: nums = [2,1,5,6,0,9,5,0,3,8], firstLen = 4, secondLen = 3
输出: 31
解释: 子数组的一种选择中,[5,6,0,9] 长度为 4,[0,3,8] 长度为 3。
提示:
1 <= firstLen, secondLen <= 1000
2 <= firstLen + secondLen <= 1000
firstLen + secondLen <= nums.length <= 1000
0 <= nums[i] <= 1000
个人思路
思路与算法
解法一 动态规划+滑块窗口
首先题目给出一个长度为 的数组 。现在我们需要返回两个长度分别为 和 的非重叠的子数组的最大和,,其中这两段子数组的前后顺序没有要求。
由于两段子数组的前后顺序没有区别,所以现在不妨设长度为 的子数组在长度为 的子数组前来计算此时的两段子数组的最大和。首先我们用 来表示 这一段子数组,并记 表示子数组 的和,为 中长度为 的最大子数组和,若不存在长度为 的子数组则为 。那么对于某一段长度为 的子数组 ,,所以此时的两个数组的最大和为
又因为
由于现在长度为 在长度为 的后面,所以用两个大小为 和 的滑动窗口分别从位置 和 同时开始从左往右滑动,并在过程中维护窗口中的和。因为对于 ,有 ,并当 时为初始第一个窗口的和。那么在两个窗口从左到右移动的过程中,通过移动第一个窗口来更新 值,通过第二个窗口来计算此时的最大和,并记录移动过程中的最大值即可。
同理我们可以得到当 的子数组在长度为 的子数组前时,两段子数组的最大和,两种情况取较大值即为最终的答案。由于 的求解只与 有关,所以在实现的过程中我们可以通过「滚动数组」来进行空间优化。
代码
class Solution {
public:
int help(vector<int>& nums, int firstLen, int secondLen) {
int suml = accumulate(nums.begin(), nums.begin() + firstLen, 0);
int maxSumL = suml;
int sumr = accumulate(nums.begin() + firstLen, nums.begin() + firstLen + secondLen, 0);
int res = maxSumL + sumr;
for (int i = firstLen + secondLen, j = firstLen; i < nums.size(); ++i, ++j) {
suml += nums[j] - nums[j - firstLen];
maxSumL = max(maxSumL, suml);
sumr += nums[i] - nums[i - secondLen];
res = max(res, maxSumL + sumr);
}
return res;
}
int maxSumTwoNoOverlap(vector<int>& nums, int firstLen, int secondLen) {
return max(help(nums, firstLen, secondLen), help(nums, secondLen, firstLen));
}
};
每天记录一下做题思路。