动态规划精讲(一)环形子组数的最大和

918. 环形子数组的最大和

题目:

给定一个由整数数组 A 表示的环形数组 C,求 C 的非空子数组的最大可能和。

在此处,环形数组意味着数组的末端将会与开头相连呈环状。(形式上,当0 <= i < A.length 时 C[i] = A[i],且当 i >= 0 时 C[i+A.length] = C[i])

此外,子数组最多只能包含固定缓冲区 A 中的每个元素一次。(形式上,对于子数组 C[i], C[i+1], ..., C[j],不存在 i <= k1, k2 <= j 其中 k1 % A.length = k2 % A.length)

示例 1:

输入:[1,-2,3,-2]
输出:3
解释:从子数组 [3] 得到最大和 3
示例 2:

输入:[5,-3,5]
输出:10
解释:从子数组 [5,5] 得到最大和 5 + 5 = 10
示例 3:

输入:[3,-1,2,-1]
输出:4
解释:从子数组 [2,-1,3] 得到最大和 2 + (-1) + 3 = 4
示例 4:

输入:[3,-2,2,-3]
输出:3
解释:从子数组 [3] 和 [3,-2,2] 都可以得到最大和 3
示例 5:

输入:[-2,-3,-1]
输出:-1
解释:从子数组 [-1] 得到最大和 -1

思路:

Kanade 算法介绍

Kadane 算法的伪代码如下

#Kadane's algorithm
ans = cur = None
for x in A:
    cur = x + max(cur, 0)
    ans = max(ans, cur)
return ans

方法 1:邻接数组

代码:

class Solution {
    public:
    int maxSubarraySumCircular(vector<int> nums) {
        int n =nums.size();
        int ans=nums[0],cur = nums[0];
        //先求左单区间子段,这里包括了整个区间的情况,所以后面的双区间子段不能包括整个区间所以是i+2;
        for (int i = 1; i < n; i++)
        {
            cur = nums[i]+max(cur,0);
            ans = max(ans,cur);
        }
        // cout<<"ans ="<<ans<<endl;
        vector<int>rightsums(n);
        rightsums[n-1]=nums[n-1];
        for (int i = n-2; i >=0; i--)
        {
            rightsums[i] = rightsums[i+1]+nums[i];
        }

        vector<int> maxright(n);
        maxright[n-1]=nums[n-1];
        for (int i = n-2; i >= 0; i--)
        {
            maxright[i]=max(maxright[i+1],rightsums[i]);
        }
        int leftsum=0;
        //第一次是比较单区间的情况,后面是双区间的比较
        for (int i = 0; i < n-2; i++)
        {
            leftsum+=nums[i];
            ans = max(ans,leftsum+maxright[i+2]);
        }
        return ans;
    }
};     

方法 3:Kadane 算法(符号变种)

 代码:

class Solution {
    public int maxSubarraySumCircular(int[] A) {
        int S = 0;  // S = sum(A)
        for (int x: A)
            S += x;

        int ans1 = kadane(A, 0, A.length-1, 1);
        int ans2 = S + kadane(A, 1, A.length-1, -1);
        int ans3 = S + kadane(A, 0, A.length-2, -1);
        return Math.max(ans1, Math.max(ans2, ans3));
    }

    public int kadane(int[] A, int i, int j, int sign) {
        // The maximum non-empty subarray for array
        // [sign * A[i], sign * A[i+1], ..., sign * A[j]]
        int ans = Integer.MIN_VALUE;
        int cur = Integer.MIN_VALUE;
        for (int k = i; k <= j; ++k) {
            cur = sign * A[k] + Math.max(cur, 0);
            ans = Math.max(ans, cur);
        }
        return ans;
    }
}

 

 优化:

class Solution {
public:
    int maxSubarraySumCircular(vector<int>& A) {
        int sofarmax = A[0],sofarmin = A[0],sum = A[0],left = A[0],right = A[0];
        for (int i = 1; i < A.size(); ++i) {
            sum += A[i];
            sofarmax = max(A[i],sofarmax + A[i]);
            sofarmin = min(A[i],sofarmin + A[i]);
            right = max(right,sofarmax);
            left = min(left,sofarmin);
        }
        if(right < 0)
            return right;
        return max(right,sum - left);
    }
};

 

因上求缘,果上努力~~~~ 作者:每天卷学习,转载请注明原文链接:https://www.cnblogs.com/BlairGrowing/p/13942313.html

原文地址:https://www.cnblogs.com/BlairGrowing/p/13942313.html