目录
题十八 最大子数组和
1、算法解析
1、确定状态:
2、状态转移方程:
3、初始化:
4、填表顺序:
5、返回值:
2、代码
题十九 环形子数组的最大和
1、算法解析
1、确定状态:
2、状态转移方程:
3、初始化:
4、填表顺序:
5、返回值:
2、代码
题二十 乘积最大子数组
1、算法解析
1、确定状态:
2、状态转移方程:
3、初始化:
4、填表顺序:
5、返回值:
2、代码
题二十一 乘积为正数的最长子数组
题十八 最大子数组和
53. 最大子数组和 - 力扣(LeetCode)
1、算法解析
1、确定状态:
dp[i]位置的值,表示以i为结尾的子数组的最大和。注意,是以i为结尾,并没有规定从那个位置开始。
2、状态转移方程:
i位置有两种状态:长度为1,长度大于1。
 长度为1时,以i结尾,就是i位置的值,即nums[i]
 长度大于1时,以i结尾,就是要包括前面i-1位置的和,即dp[i-1] + nums[i]
3、初始化:
初始化解决的是填表越界的问题
 根据状态转移方程,我们需要初始化dp[0]的位置
 第一个,因为必须包含一个元素,所以只能是其本身值
 即dp[0] = n[0]
4、填表顺序:
从左往右
5、返回值:
我们不知道最大值到底是以那个位置为结尾的连续子数组,因此要遍历拿出最大值。
2、代码
class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        //1、创建dp表
        int n = nums.size();
        if(n==1) return nums[0];
        vector<int> dp(n);
        //2、初始化
        dp[0] = nums[0];
        //3、填表
        for(int i = 1; i<n; ++i)
        {
            dp[i] = max(nums[i], dp[i-1] + nums[i]);
        }
        //4、返回值
        int ret = -0x3f3f3f3f;
        for(int i = 0; i<n; ++i)
        {
            if(dp[i] >= ret)
            {
                ret = dp[i];
            }
        }
        return ret;
    }
};题十九 环形子数组的最大和
918. 环形子数组的最大和 - 力扣(LeetCode)
1、算法解析
这一题比简单的线性最大数组和多了一个环形。
 我们怎么做呢?
 可以不可以把环形,变成线性的数组?
 如果转化为线性的数组,那就是最大连续子数组和
 我们仔细研究一下这个环形,会发现:答案就只有两种可能
 一个是最大连续字数组就在中间位置:
 一个是最大连续子数组在两边连接处:
这第二种情况,我们会发现中间的连续子数组是最小的。
 那么数组的最大值就是整个数组的和-最小连续子数组。
 那么,我们就来比较这两种情况那个大即可。
1、确定状态:
我们需要两个表,一个f求最小值,一个g求最大值:
 f[i]位置的值,表示以i为结尾的最小连续子数组和。注意,是以i为结尾,并没有规定从那个位置开始。
 g[i]位置的值,表示以i为结尾的最大连续子数组和。注意,是以i为结尾,并没有规定从那个位置开始。
2、状态转移方程:
i位置有两种状态:长度为1,长度大于1。
 长度为1时,以i结尾,就是i位置的值,即nums[i]
 长度大于1时,以i结尾,就是要包括前面i-1位置的和,即f[i-1] + nums[i]
f[i] = min(nums[i], f[i-1] + nums[i]);
 g[i] = max(nums[i], g[i-1]  + nums[i]);
 一个求最大,一个求最小。
3、初始化:
初始化解决的是填表越界的问题
 根据状态转移方程,我们需要初始化f[0]的位置
 f[0] = nums[0]
 g[0] = nums[0]
4、填表顺序:
从左往右
5、返回值:
找出最大值,和sum-最小值对比,返回大者。
 但是,需要注意,如果所有的数组都是负数,最小值就是所有数字的和。
 sum - min = 0;就会返回0
 但是返回值不能是0,因为必须包含一个元素。
 所以需要特别判断
2、代码
  
 
class Solution {
public:
    int maxSubarraySumCircular(vector<int>& nums) {
        //1、创建dp表
        int n = nums.size();
        if(n == 1) return nums[0];
        vector<int> f(n);
        auto g = f; 
        int sum = 0;
        for(auto e : nums)
        {
            sum += e;
        }
        //2、初始化
        f[0] = nums[0];
        g[0] = nums[0];
        //3、填表
        for(int i = 1; i<n; ++i)
        {
            f[i] = min(nums[i], f[i-1] + nums[i]);//找最小值
            g[i] = max(nums[i], g[i-1] + nums[i]);//找最大值
        }
        //4、返回值
        int max_value  = -0x3f3f3f3f;
        int min_value = 0x3f3f3f3f;
        for(int i= 0; i<n; ++i)
        {
            if(g[i] >= max_value)
                max_value = g[i];
            if(f[i] <= min_value)
                min_value = f[i];
        }
        
        if(sum == min_value)
            return max_value;
        
        return max(max_value, sum - min_value);
    }
};题二十 乘积最大子数组
152. 乘积最大子数组 - 力扣(LeetCode)
1、算法解析
1、确定状态:
也是一个连续子数组求最大最小的问题。
 我们创建一个一维dp表。
 dp[i]的值,表示以i为结尾的最大乘积子数组,注意,并没有规定是从那个位置作为开始位置。
2、状态转移方程:
i位置有两种状态:长度为1,长度大于1。
 长度为1时,以i结尾,就是i位置的值,即nums[i]
 长度大于1时,以i结尾,就是要包括前面i-1位置的和,即dp[i-1] * nums[i]
按照原来的逻辑,上述的推导是没有问题的。
 但是,如果nums[i]是一个负数,dp[i-1]是一个最大值,那么最大值×负数,直接变成最小值。
 出问题了。
 正确的逻辑应该是:
 如果nums[i]是一个正数,那么dp[i-1]的值,应该是最大值
 如果nums[i]是一个负数,那么dp[i-1]的值,应该是最小值。
 所以,按照我们的逻辑推导,我们需要两个状态表:
 f[i]代表表示以i为结尾的最大乘积子数组
 g[i]代表表示以i为结尾的最小乘积子数组
 如图:
3、初始化:
初始化解决的是填表越界的问题
 根据状态转移方程,我们需要初始化f[0]的位置
 f[0] = g[0] = nums[0];
4、填表顺序:
从左往右
5、返回值:
返回f表中的最大值。
2、代码
class Solution {
public:
    int maxProduct(vector<int>& nums) {
        //1、创建dp表
        int n = nums.size();
        if(n == 1) return nums[0];
        vector<int > f(n);
        auto g =f;
        //2、初始化
        f[0] = g[0] = nums[0];
        //f[i]为最小值
        //g[i]为最大值
        //3、填表
        for(int i = 1; i<n; ++i)
        {
            f[i] = min(nums[i], min(g[i-1] * nums[i], f[i-1] * nums[i]));
            g[i] = max(nums[i], max(f[i-1] * nums[i], g[i-1] * nums[i]));
        }
        
        //4、返回值
        int  ret = INT_MIN;
        for(int i = 0; i<n; ++i)
        {
            if(g[i] >= ret) 
                ret = g[i];
        }
        return ret;
    }
};
题二十一 乘积为正数的最长子数组
152. 乘积最大子数组 - 力扣(LeetCode)



















