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

## 问题描述

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

[link]: https://leetcode.cn/problems/maximum-sum-circular-subarray/

给定一个长度为 `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$；

## 代码

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

