问题描述
918. 环形子数组的最大和 (Medium)
给定一个长度为 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
。
示例 1:
输入:nums = [1,-2,3,-2]
输出:3
解释:从子数组 [3] 得到最大和 3
示例 2:
输入:nums = [5,-3,5]
输出:10
解释:从子数组 [5,5] 得到最大和 5 + 5 = 10
示例 3:
输入:nums = [3,-2,2,-3]
输出:3
解释:从子数组 [3] 和 [3,-2,2] 都可以得到最大和 3
提示:
n == nums.length
1 <= n <= 3 * 10⁴
-3 * 10⁴ <= nums[i] <= 3 * 10⁴
解题思路
我们令 $dp[i]$ 表示以 $nums[i]$ 结尾的子数组的最大和,那么我们分两种情况讨论即可:
- 子数组是连续的一段,即 $tail >= head$;
- 子数组被分成了两段,即 $tail < head$;
代码
class Solution {
public:
int maxSubarraySumCircular(vector<int> &nums) {
int n = nums.size();
if (n == 1) {
return nums[0];
}
vector<int> dp(n, INT_MIN);
// 先求 dp[0];
// 分情况讨论,先统计正常的情况,再统计被分成两段的情况
int sum = accumulate(nums.begin(), nums.end(), 0);
vector<int> normal(n, INT_MIN);
dp[0] = nums[0];
for (int i = 1; i < n; ++i) {
dp[i] = max(nums[i], nums[i] + dp[i - 1]);
}
// 寻找以 head 为起点的最小值
vector<int> min_sum(n, 0);
min_sum[n - 1] = nums[n - 1];
for (int i = n - 2; i >= 0; --i) {
min_sum[i] = min(min_sum[i + 1] + nums[i], nums[i]);
}
int res = INT_MIN;
for (int i = 0; i < n - 1; ++i) {
dp[i] = max(dp[i], sum - min_sum[i + 1]);
res = max(res, dp[i]);
}
return max(res, dp[n - 1]);
}
};
标签:Medium,nums,int,sum,min,环形,918,数组,dp
From: https://www.cnblogs.com/zwyyy456/p/17573529.html