动态规划6,最大数组和,环形子数组最大和,乘积最大子数组

最大子数组和

在这里插入图片描述
思路:

1.经验+题目要求

dp[i]表示:以 i 位置为结尾的所有子数组中的最大和

2.状态转移方程

按长度来划分,如果长度为1,那么dp[i] = nums[i];
如果长度大于1,那么当前位置的最大和就为 i-1 位置最大和 + 当前位置,dp[i] = dp[i-1] + nums[i];

然后每一次都要取他们两个中的最大值。

在这里插入图片描述

存在 dp[i-1] , 建表时候多建一格,dp[0]位置为0 就不影响后面的填表
在这里插入图片描述

4 .
从左往右填表。

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
           int n = nums.size();
           vector<int> dp(n+1);
           
           int ret = INT_MIN;//为了不影响比较
           for(int i = 1 ; i<=n; i++)
           {
               dp[i] = max(nums[i-1],dp[i-1]+nums[i-1]);
               ret = max(dp[i],ret);//找出dp表里面的最大值
           }

           return ret;
    }
};

环形子数组的最大和

在这里插入图片描述
思路:

区别于上一道题,这一题变成了环形,也就是多了两边的情况,而两边的情况很复杂,我们可不可以把两边问题换为两种上一道题思路的简单问题?

在这里插入图片描述
最大数组和只有两种情况,看 1 在里面的情况,这就跟上一道题一样
看 2 ,当最大数组和在两边的时候,我们可以求出最小数组和的情况,然后sum - min。

步骤及思路都跟上一题一样,无非变成了求一个最大和表和一个最小和表,然后比较max(最大和,sum-最小和);

但是对于返回值,有点变化:
在这里插入图片描述
对于[-2 , -3 , -1], 最大和为-1, 但是最小和为-2 + -3 + -1 ,和sum是一样的,这时候最小和就变为了错误的0;
所以对于返回值要处理, 最小和 == sum的时候,返回最大和,不然返回max(最大和,sum-最小和)。

class Solution {
public:
    int maxSubarraySumCircular(vector<int>& nums) {
        int n = nums.size();
        vector<int> f(n+1);//建表
        auto g = f;

        int ret1 = INT_MIN;//不影响比较
        int ret2 = INT_MAX;
        int num = 0;
        for(int i = 1; i<=n; i++)
        {
            f[i] = max(nums[i-1],f[i-1]+nums[i-1]);
            ret1 = max(ret1,f[i]);
            g[i] = min(nums[i-1],g[i-1]+nums[i-1]);
            ret2 = min(ret2,g[i]);
            num+=nums[i-1]; //求nums和
        }

        return num == ret2 ? ret1 : max(ret1,num - ret2);
    }
};

乘积最大子数组

在这里插入图片描述
思路:

1.经验+题目要求

dp[i]表示:以 i 位置为结尾的所有子数组中的最大乘积

2.状态转移方程

按长度来划分,如果长度为1,那么dp[i] = nums[i];
如果长度大于1,那么当前位置的最大和就为 i-1 位置最大和 + 当前位置,dp[i] = dp[i-1] * nums[i];

但是因为有正负,前面可能还是乘积最大,后一个数为负数,一下就变成了乘积最小;
相反,前面乘积最小,后一个数为负数,一下就就变成了乘积最大。

所以也要建一个乘积最小表。

在这里插入图片描述
f 表是乘积最大表,g 表是乘积最小表,
对于 f 表来讲,如果长度为1,f[i] = nums[i];
如果长度大于1,但是下一个数大于0,那么f[i] = f[i-1] * nums[i];
如果长度大于1,但是下一个数小于0,那么f[i] = g[i-1] * nums[i];
比较这三者的最大值并填入 f 表即可。 g表同理。

在这里插入图片描述
为了不影响乘积之间的填表,初始化f[0] = g[0] = 1就可以。

4.从左往右,两个表一起填写。

class Solution {
public:
    int maxProduct(vector<int>& nums) {
        int n = nums.size();
        vector<int> f(n+1);
        auto g = f;

        int ret1 = INT_MIN;
        int ret2 = INT_MAX;
        f[0] = g[0] = 1;
        for(int i = 1; i<=n; i++)
        {
           f[i] = max(nums[i-1],max(f[i-1]*nums[i-1], g[i-1]*nums[i-1]));
           ret1 = max(f[i],ret1);
           g[i] = min(nums[i-1],min(g[i-1]*nums[i-1], f[i-1]*nums[i-1]));
           ret2 = min(g[i],ret2);
        }

        return ret1;
    }
};

乘积为正数的最长子数组长度

在这里插入图片描述
思路:

1.经验+题目要求

dp[i]表示:以 i 位置为结尾的所有子数组中乘积为正数的最长长度。

2.状态转移方程
在这里插入图片描述
首先细分,当长度为1的时候,如果nums[i] > 0 ,则为1;
当长度为1的时候,如果nums[i] < 0 ,则为0;
当长度大于1的时候,如果nums[i] > 0 ,则为f[i-1] + 1;
当长度大于1的时候,如果nums[i] < 0 ,则为g[i-1] + 1;但是这个g[i-1] + 1真的对吗?当g[i-1] 为0的时候,也就是前面乘积为正数,乘完nums[i] 后长度就变成0了,但是g[i-1] + 1结果为1,明显是错误的,所以应该判断 g[i-1] == 0 ? 0 : g[i-1] + 1;

再次划分情况,对于nums[i] > 0 的情况,f[i-1] + 1最次也为1,占主导;
对于nums[i] < 0 的情况,g[i-1] == 0 ? 0 : g[i-1] + 1;最次也为0,占主导;
就可以总结为下面那两种占主导的情况。

g[i] 也是如此。
在这里插入图片描述
3.
因为题目问的是长度,初始化f[0] = g[0] = 0;
在这里插入图片描述
4.从左往右,两个表一起填写。

class Solution {
public:
    int getMaxLen(vector<int>& nums) {
        int n = nums.size();
        vector<int> f(n+1);
        auto g = f;
        int ret = INT_MIN;
        for(int i =1; i<=n; i++)
        {
            if(nums[i-1] > 0)
            {
                f[i] = f[i-1]+1;
                g[i] = g[i-1] == 0 ? 0 : g[i-1]+1;
            }
            else if(nums[i-1] < 0)
            {
                f[i] = g[i-1] == 0 ? 0 : g[i-1]+1;
                g[i] = f[i-1]+1;
            }
            ret = max(ret,f[i]);
        }

        return ret;
    }
};
  • 22
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值