918. 环形子数组的最大和 (Medium)

发布时间 2023-07-22 16:10:54作者: zwyyy456

问题描述

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]);
    }
};