力扣hot100:152.乘积最大子数组(动态规划)

一个子数组问题,我们要使用线性dp,最好先考虑以i结尾,如果定义dp[i]为前i个数最大子数组乘积值 那么dp[i-1]就无法转移到dp[i]。因此我们先考虑dp[i]定义为以第i个数结尾的最大子数组乘积值。
53. 最大子数组和

最大子数组和是一个动态规划问题,定义dp[i]表示以nums[i]结尾的最大子数组和,那么dp[i]=max(dp[i-1]+nums[i],nums[i])。对于这里乘积最大子数组和,我们也有这样的想法,但是由于负负得正,如{-3,2,3,-2},dp[2]=6,nums[3]=-2,但是dp[3]不是-2,而应当乘以前面的-3。

记录前一个负数位置的动态规划

一个朴素的想法就是:

记录前一个负数的位置,这样遍历到一个负数时,我们在前一个负数到这个负数之间的数都是≥0的,这样在遇到负数时的连乘最大值应当至少是前一个负数连乘到这个负数,而当 以 前一个负数的 前一个数为结尾的子数组乘积为正时,也应该考虑进去。这样负数的情况就考虑完了。当之前没有负数时,有0时dp[i]就是0,没有0时dp[i]就是该负数。

当遇到的是一个正数,则只需要使用dp[i]=max(dp[i-1]*nums[i],nums[i]),因为以该正数结尾的最大连乘,要么是本身,要么以 前一个数结尾的子数组连乘为正*该正数。

cpp 复制代码
class Solution {
public:
    int maxProduct(vector<int>& nums) {
        vector<int> dp(nums.size());
        int ans;
        ans=dp[0]=nums[0];
        int minus=-1;
        if(nums[0]<0) minus=0;
        int flag=0;//记录前一个负数到这个负数是否存在0
        for(int i=1;i<nums.size();++i){
            dp[i]=1;
            if(nums[i]==0) flag=1;
            if(nums[i]<0){
                if(minus>=0){//中间有0也应该是0
                    if(minus>0&&dp[minus-1]>0)
                        dp[i]=dp[minus-1]*nums[minus]*nums[i];
                    else dp[i]=nums[minus]*nums[i];
                    if(minus!=i-1) {
                        if(dp[minus]<=0)
                            dp[i]*=dp[i-1];
                        else dp[i]*=dp[i-1]/dp[minus];
                    }
                    if(flag) dp[i]=0;
                }else dp[i]=nums[i];
                minus=i;
                flag=0;
            }else dp[i]=dp[i-1]>0?dp[i-1]*nums[i]:nums[i];
            if(dp[i]>ans) ans=dp[i];
        }
        //cout<<dp[nums.size()-2];
        return ans;
    }
};
//dp[i]表示以i结尾的子数组的乘积最大值

记录最大最小的动态规划

进阶的考虑:

当遇到负数时,我们能不能让 以它前一个数结尾的连乘 负得更多,这样我们再乘上这个数就大的更多。

当遇到正数时,我们依然让 以前一个数结尾的连乘 正的更多即可。

因此,我们可以保存一个最小值和最大值。

最小值让以第i个数结尾的子数组连乘最小,

最大值让以第i个数结尾的子数组连乘最大,

最小值的计算和最大值的计算,前一两者同时考虑就把正负给抵消掉了。

cpp 复制代码
class Solution {
public:
    int maxProduct(vector<int>& nums) {
        int mx=nums[0];
        int mn=nums[0];
        int ans=nums[0];
        for(int i=1;i<nums.size();++i){
            int Max=mx,Min=mn;
            mx=max(max(Max*nums[i],Min*nums[i]),nums[i]);
            mn=min(min(Min*nums[i],Max*nums[i]),nums[i]);
            ans=ans>mx?ans:mx;
        }
        return ans;
    }
};
相关推荐
m0_5719575828 分钟前
Java | Leetcode Java题解之第543题二叉树的直径
java·leetcode·题解
奋斗的小花生2 小时前
c++ 多态性
开发语言·c++
pianmian13 小时前
python数据结构基础(7)
数据结构·算法
闲晨3 小时前
C++ 继承:代码传承的魔法棒,开启奇幻编程之旅
java·c语言·开发语言·c++·经验分享
UestcXiye4 小时前
《TCP/IP网络编程》学习笔记 | Chapter 3:地址族与数据序列
c++·计算机网络·ip·tcp
好奇龙猫5 小时前
【学习AI-相关路程-mnist手写数字分类-win-硬件:windows-自我学习AI-实验步骤-全连接神经网络(BPnetwork)-操作流程(3) 】
人工智能·算法
霁月风5 小时前
设计模式——适配器模式
c++·适配器模式
sp_fyf_20245 小时前
计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-11-01
人工智能·深度学习·神经网络·算法·机器学习·语言模型·数据挖掘
香菜大丸6 小时前
链表的归并排序
数据结构·算法·链表
jrrz08286 小时前
LeetCode 热题100(七)【链表】(1)
数据结构·c++·算法·leetcode·链表