leetcode 21天动态规划入门——从0到0.5【Day06】乘积最大子数组数组 以及乘积为正的最大子数组长度 dp优化

leetcode 21天动态规划入门——从0到0.5【Day06】乘积最大子数组数组 以及乘积为正的最大子数组长度 dp优化

DAY06

写在前面

今天是动态规划入门的第六天了,今天的题稍微有一丢丢难,需要考虑到相关的优化,如果觉得不适,那么就掌握最简单的那种方法就好啦,最近博主在学一些技术,这几天可能只会暂时更新每日一题以及动态规划入门这两个板块了,Java基础的知识可能会在下个月博主放假的时候再开始更新(一月7号前后)谢谢大家哦~同时小付也理解技术知识不光需要了解了基础知识之外,还需要拓展以后业务中可能出现的情况,小付会在之后一一改进,话不多说,冲就完事了!

题目

题目一
  1. 乘积最大子数组
给你一个整数数组 nums ,请你找出数组中乘积最大的连续子数组
(该子数组中至少包含一个数字),并返回该子数组所对应的乘积。
示例

示例1:

输入: [2,3,-2,4]
输出: 6
解释: 子数组 [2,3] 有最大乘积 6。

示例2:

输入: [-2,0,-1]
输出: 0
解释: 结果不能为 2, 因为 [-2,-1] 不是子数组。
思路
经过几天的动态规划训练,应该入门基础的思想了,
不过针对此题,之前的思想虽然还可以适用,但是已经快要无法满足
时间与空间的需求了,这有违背我们学习算法的初衷,
不过最简单的思想还是要会:
1)今天可不只能维护一个数组dp了,
因为你要考虑如果当前的乘积和是最小子数组乘积和时,
如果下一个数是负数再相乘时,此时就会得到一个相对较大的数值,
就要和最大的子数组乘积和相比较取最大值。
2)同理如果此时得到的是最大子数组乘积和时,
如果下一个数是负数再相乘时,此时又会得到一个相对较小的数值,
所以最后随数组滚动时需要不断进行比较,
就会导致时间以及空间复杂度大大提高。

动态转化方程:
用来获取当前乘积最大值:
dpMax[i] = Math.max(nums[i], Math.max(dpMax[i - 1] * nums[i], dpMin[i - 1] * nums[i]));
用来获取当前乘积最小值:
dpMin[i] = Math.min(nums[i], Math.min(dpMin[i - 1] * nums[i], dpMax[i - 1] * nums[i]));

代码实现
class Solution {
    public int maxProduct(int[] nums) {
       int res = nums[0];
       int[] dpMax = new int[nums.length + 1], dpMin = new int [nums.length + 1];
       dpMax[0] = nums[0];  // 初始化两个dp数组
       dpMin[0] = nums[0];
       for(int i = 1; i < nums.length; i++)
       { 
           dpMax[i] = Math.max(nums[i], Math.max(dpMax[i - 1] * nums[i], dpMin[i - 1] * nums[i])); //获取乘积最大值
           dpMin[i] = Math.min(nums[i], Math.min(dpMin[i - 1] * nums[i], dpMax[i - 1] * nums[i])); //获取乘积最小值
           res = Math.max(res, dpMax[i]);
       }         
       return res;   
    }
}
执行结果

在这里插入图片描述
还没我家小狗狗吃得少跑得快呢~

优化

既然上面思路都提到了因为维护数组消耗了大量空间时间,那么我们可以利用变量来模拟当前状况从而节省空间与时间

class Solution {
    public int maxProduct(int[] nums) {
    	//设计imax、imin来获取当前子数组的最大值与最小值
        int max = nums[0],imax = 1 ,imin = 1;
        for (int i = 0 ; i< nums.length ;i++){
        	//如果当前需要乘的数<0那么最大将变成最小的 将其进行交换方便变量进行处理
            if (nums[i] < 0){
                int temp = imax ;
                imax = imin;
                imin = temp;
            }
            //如果当前需要乘的数<0那么最大将变成最小的 最小的变成最大的
            imax = Math.max(imax*nums[i],nums[i]);
            imin = Math.min(imin*nums[i],nums[i]);

            max = Math.max(imax,max);
        }
        return max;

    }
}
优化结果

在这里插入图片描述

题目二
示例

示例1:

输入:nums = [1,-2,-3,4]
输出:4
解释:数组本身乘积就是正数,值为 24 。

示例2:

输入:nums = [1,-2,-3,4]
输出:4
解释:数组本身乘积就是正数,值为 24 。

示例3:

输入:nums = [-1,-2,-3,0,1]
输出:2
解释:乘积为正数的最长子数组是 [-1,-2] 或者 [-2,-3] 。

示例4:

输入:nums = [-1,2]
输出:1
提示

1 <= nums.length <= 10^5
-10^9 <= nums[i] <= 10^9

思路
这道题需要进行分类讨论
1)当前需要进行乘的值如果等于0则正负最长子数组长度均需重新置零
重新从下一个数进行遍历
2)当此时乘数为正时,此时维护的这个正子数组和长度+1
且需要为下次迎接乘数为负数做准备,使得负数长度也+1
3)如果当前乘数为负数,则需要先记录当前正数的子数组长度,
此时正数子数组长度需要为下一次迎接负数做准备+1
代码实现
class Solution {
    public int getMaxLen(int[] nums) {
    	//维护当前正数子数组长度与负数子数组长度
        int posLen = 0;
        int negLen = 0;
        int max = 0;
        //依次遍历分类讨论
        for (int i : nums){
            if (i == 0){
                posLen = 0;
                negLen = 0;
                continue;
            }
            //当前乘数大于0时
            if (i > 0){
                posLen++;
                negLen = negLen == 0? 0:negLen+1;
            }
			//当前乘数小于0时
            if (i < 0){
                int temp = posLen;
                posLen = negLen == 0 ? 0 :negLen+1;
                negLen = temp+1;
            }
            //滚动变量用来记录最大值
            max = Math.max(max,posLen);
        }
        return max;
    }
}
执行结果

在这里插入图片描述

写在最后

今日的动态规划相较于昨天已经有了很大的改变,
不能再单一想通过一个数组或者变量来维护所需求解的值了,
也需要通过数学的相关知识来分类讨论进行题解
坚持打卡第六天~
加油 ~

最后

每天进步点 每天收获点

愿诸君 事业有成 学有所获

如果觉得不错 别忘啦一键三连哦~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Alascanfu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值