Leetcode 53 152 最大子序和&乘积

本文探讨了动态规划在解决连续子数组最大和问题上的应用,以及如何处理连续子数组乘积最大值的问题。对于乘积问题,由于负负得正的特性,单纯建表无法解决,需要采用两次遍历或动态规划方法。文章提供了不同算法的代码实现,并分析了处理负数和零时的策略。
摘要由CSDN通过智能技术生成

在这里插入图片描述
本题可以通过动态规划建表,在O(N) 时间内解决。
表中的元素 d p [ i ] dp[i] dp[i] 代表以第 i i i 个数结尾的连续子数字的最大和。由于子数组是连续的,第 i i i 个元素为结尾仅有两种,要么是其本身,要么是在前一个为元素结尾的基础上加上其本身,二者取最大值。因此可写出状态转移方程: d p [ i ] = m a x ( d p [ i ] + n u m s [ i ] , n u m s [ i ] ) dp[i] = max(dp[i] + nums[i], nums[i]) dp[i]=max(dp[i]+nums[i],nums[i])附上代码:

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        vector<int> dp(nums.size(), nums[0]);
        int maximum = nums[0];

        for(int i=1; i<nums.size(); i++) {
            dp[i] = max(dp[i-1]+nums[i], nums[i]);
            maximum = max(maximum, dp[i]);
        }
        return maximum;
    }
};

下面一个题,把和换成了乘积。
在这里插入图片描述
可能一看这个题就会想到:哎,这不和刚才那个差不多,建表存储以第 i i i个数为结尾的乘积较大值,统计输出最大值就完事了!
然而,这样做是完全不对的!!
在加法中,负数无论如何都只会对总和产生负面的影响,
而在乘法中,存在负负得正的情况。负数遇到新的负数乘积会变成更大的正数。
除dp建表之外,还可以使用两次遍历(正向遍历+反向遍历)解决该问题。

由于所有的数组都为整数,若不看正负只看绝对值的情况,每乘新的数字都不会使总乘积变小(0除外)即
若数组 n u m s nums nums中不存在零,且负数的个数为偶数个时,将所有数相乘时得到最大值。
反之,若负数的个数为奇数,则在某一个负数的位置“切一刀”,两边的乘积较大的一个即为最大值。
’切一刀‘之后,最大值的取得总是从数组的两端开始乘起的,因此可以使用正反两次遍历累乘得到。
初始将 c u r r e n t current current 设为1,不断与遇到的数字相乘,记录最大值。若遇到零, c u r r e n t current current 设为1继续遍历。

附上代码:

class Solution {
public:
    int maxProduct(vector<int>& nums) {
        int current = 1, maximum = INT_MIN;
        for(int i=0; i<nums.size(); i++) {
            current *= nums[i];
            maximum = max(maximum, current);
            if(nums[i] == 0) current = 1;
        }
        
        current = 1;
        for(int i = nums.size()-1; i>=0; i--) {
            current *= nums[i];
            maximum = max(maximum, current);
            if(nums[i] == 0) current = 1;
        }

        return maximum;
    }
};

另外一种较为传统的动态规划算法,用两个数组,分别记录了以第 i i i 个数为结尾的乘积的最大值和最小值,用来应对负负得正的情况,状态转移方程为:
d p m a x [ i ] = m a x ( n u m s [ i ] × d p m a x [ i − 1 ] , n u m s [ i ] × d p m i n [ i ] , n u m s [ i ] ) dp_{max}[i] = max(nums[i]\times dp_{max}[i-1], nums[i]\times dp_{min}[i], nums[i]) dpmax[i]=max(nums[i]×dpmax[i1],nums[i]×dpmin[i],nums[i]) d p m i n [ i ] = m i n ( n u m s [ i ] × d p m a x [ i − 1 ] , n u m s [ i ] × d p m i n [ i − 1 ] , n u m s [ i ] ) dp_{min}[i] = min(nums[i]\times dp_{max}[i-1], nums[i]\times dp_{min}[i-1], nums[i]) dpmin[i]=min(nums[i]×dpmax[i1],nums[i]×dpmin[i1],nums[i])附上代码:

class Solution {
public:
    int maxProduct(vector<int>& nums) {
        int maximum;
        vector<int> maxi(nums.size(), nums[0]);
        vector<int> mini(nums.size(), nums[0]);

        for(int i=1; i<nums.size(); i++) {
            
            maxi[i] = max(nums[i], nums[i]*maxi[i-1]);
            maxi[i] = max(maxi[i], mini[i-1]*nums[i]);

            mini[i] = min(nums[i], nums[i]*mini[i-1]);
            mini[i] = min(mini[i], maxi[i-1]*nums[i]);
        }

        return *max_element(maxi.begin(), maxi.end());
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值