【leetcode】152.乘积最大子数组 (动态规划详细解析,java多种方法实现)

152. 乘积最大子数组

难度中等715

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

示例 1:

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

示例 2:

输入: [-2,0,-1]
输出: 0
解释: 结果不能为 2, 因为 [-2,-1] 不是子数组。

分析

思路
  • 这个问题很像「力扣」第 53 题:最大子序和,只不过当前这个问题求的是乘积的最大值;
  • 「连续」这个概念很重要,可以参考第 53 题的状态设计,将状态设计为:nums[i]结尾的连续子数组的最大值
  • 类似状态设计的问题还有「力扣」第 300 题:最长上升子序列,「子数组」、「子序列」问题的状态设计的特点是:nums[i] 结尾,这是一个经验,可以简化讨论。

提示:nums[i] 结尾这件事情很重要,贯穿整个解题过程始终,请大家留意。

分析与第 53 题的差异
  • 求乘积的最大值,示例中负数的出现,告诉我们这题和 53 题不一样了,一个正数乘以负数就变成负数,即:最大值乘以负数就变成了最小值
  • 因此:最大值和最小值是相互转换的,这一点提示我们可以把这种转换关系设计到「状态转移方程」里去
  • 如何解决这个问题呢?这里常见的技巧是在「状态设计」的时候,在原始的状态设计后面多加一个维度,减少分类讨论,降低解决问题的难度。

这里是百度百科的「无后效性」词条的解释:

无后效性是指如果在某个阶段上过程的状态已知,则从此阶段以后过程的发展变化仅与此阶段的状态有关,而与过程在此阶段以前的阶段所经历过的状态无关。利用动态规划方法求解多阶段决策过程问题,过程的状态必须具备无后效性。

再翻译一下就是:「动态规划」通常不关心过程,只关心「阶段结果」,这个「阶段结果」就是我们设计的「状态」。什么算法关心过程呢?「回溯算法」,「回溯算法」需要记录过程,复杂度通常较高。

而将状态定义得更具体,通常来说对于一个问题的解决是满足「无后效性」的。这一点的叙述很理论化,不熟悉朋友可以通过多做相关的问题来理解「无后效性」这个概念。

第 1 步:状态设计(特别重要)
  • dp[i][j]
    

    :以nums[i]结尾的连续子数组的最值,计算最大值还是最小值由j 来表示,j 就两个值;

    • j = 0 的时候,表示计算的是最小值;
    • j = 1 的时候,表示计算的是最大值。

这样一来,状态转移方程就容易写出。

第 2 步:推导状态转移方程(特别重要)
  • 由于状态的设计 nums[i] 必须被选取(请大家体会这一点,这一点恰恰好也是使得子数组、子序列问题更加简单的原因:当情况复杂、分类讨论比较多的时候,需要固定一些量,以简化计算);
  • nums[i] 的正负和之前的状态值(正负)就产生了联系,由此关系写出状态转移方程:
    • 当 nums[i] > 0 时,由于是乘积关系:
      • 最大值乘以正数依然是最大值;
      • 最小值乘以同一个正数依然是最小值;
    • 当nums[i] < 0 时,依然是由于乘积关系:
      • 最大值乘以负数变成了最小值;
      • 最小值乘以同一个负数变成最大值;
    • nums[i] = 0 的时候,由于 nums[i] 必须被选取,最大值和最小值都变成 00 ,合并到上面任意一种情况均成立。
  • 但是,还要注意一点,之前状态值的正负也要考虑:例如,在考虑最大值的时候,当 nums[i] > 0 是,如果 dp[i - 1][1] < 0 (之前的状态最大值) ,此时 nums[i] 可以另起炉灶(这里依然是第 53 题的思想),此时 dp[i][1] = nums[i] ,合起来写就是:
dp[i][1] = max(nums[i], nums[i] * dp[i - 1][1]) if nums[i] >= 0

其它三种情况可以类似写出,状态转移方程如下:

dp[i][0] = min(nums[i], nums[i] * dp[i - 1
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值