leecode #152 乘积最大子数组 | 刷题之路第二站——动态规划类问题

题号 152

题目描述

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

示例1:

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

示例2:

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

 

解题思路一——暴力法

对于给定数组 nums 中的每个元素 nums[ i ],分别计算:

nums[ 0 ] ~ nums[ i  ] 所有元素的乘积

nums[ 1 ] ~ nums[ i ]  所有元素的乘积

……

nums[ i - 1 ] 和 nums[ i ] 的乘积

nums[ i ] 自身的值

从上述所有结果中,挑选值最大的存入数组 dp[ i ] 中,dp[ i ] 代表以元素 nums[ i ] 结尾的连续子数组的最大乘积值。

在 nums 中所有元素都被遍历完成后,从数组 dp 挑选数值最大值就是最终结果。

代码如下:

class Solution {
public:
	int maxProduct(vector<int>& nums) {
		int len = nums.size();
		if (len == 0) return 0;
		if (len == 1) return nums[0];
		vector<int> dp(len);    //数组dp用于记录以元素nums[i]结尾的连续子数组乘积的最大值
		dp[0] = nums[0];
		for (int i = 1; i < len; i++) {
			int max = nums[i];
			int mid = nums[i];
			for (int j = i - 1; j >= 0; j--) {
				mid = mid * nums[j];
				if (mid > max)
					max = mid;
			}
			dp[i] = max;
		}
		return *max_element(dp.begin(), dp.end());
	}
};

时间复杂度:O(n^2)

空间复杂度:O(n)

 

解题思路二——动态规划

初始想法:

数组 dp[ i ] 用于记录以 nums[ i ]  结尾来的连续子数组乘积的最大值,考虑如何从 dp[ i - 1 ] 推导出 dp[ i ]:

(1)如果 nums[ i ] > 0,dp[ i - 1 ] > 0,则 dp[ i ] = nums[ i ] × dp[ i - 1];

(2)如果 nums[ i ] > 0,dp[ i - 1 ] <= 0, 则 dp[ i ] = nums[ i ];

(3)如果 nums[ i ] == 0,则 dp[ i ] = nums[ i ];

(4)如果 nums[ i ] < 0,dp[ i - 1 ] > 0,则 dp[ i ] = nums[ i ];

(5)如果 nums[ i [ < 0,dp[ i - 1] <= 0,则 dp[ i ] = nums[ i ] × dp[ i - 1 ]。

综上,dp[ i ] = max( nums[ i ] × dp[ i - 1 ],nums[ i ])。

但是,这种想法是错误的,因为不满足【最优子结构】的性质。

举个栗子:

nums = { -2, 3, -4 }

按照上述方法计算得到的 dp 数组为: dp = { -2, 3, -4 }

但是对于元素 4 而言,其乘积最大的连续子数组是 { -2,3,-4} ,结果为 24,而不是按照上述方法计算得到的 4 。

 

正式思路:

如果 nums[ i ] 是正数,则我们希望以 nums[ i - 1 ] 结尾的连续子数组的乘积也为正数,并且越大越好;

如果 nums[ i ] 是负数,则我们希望以 nums[ i - 1 ] 结尾的连续子数组的乘积也为负数,并且越小越好(负负得正,这样得到的乘积才最大)

所以,我们考虑维护两个数组:

数组 dp_max[ i ] 用于存储以 nums[ i ] 结尾的连续子数组的乘积的最大值;

数组 dp_min[ i ] 用于存储以 nums[ i ] 结尾的连续子数组的乘积的最小值;

推导公式:

dp_max[ i ] = max( nums[ i ] × dp_max[ i - 1 ],nums[ i ] × dp_min[ i - 1 ],nums[ i ] )

dp_min[ i ] = min( nums[ i ] × dp_min[ i - 1],nums[ i ] × dp_min[ i - 1 ],nums[ i ] )

问题的底:

dp_max[ 0 ] = nums[ 0 ],dp_min[ 0 ] = nums[ 0 ]

 

代码如下:

class Solution {
public:
	int maxProduct(vector<int>& nums) {
		int len = nums.size();
		vector<int> dp_max(len), dp_min(len);
		dp_max[0] = nums[0], dp_min[0] = nums[0];
		for (int i = 1; i < len; i++) {
			dp_max[i] = max(dp_max[i - 1] * nums[i], max(dp_min[i - 1] * nums[i], nums[i]));
			dp_min[i] = min(dp_max[i - 1] * nums[i], min(dp_min[i - 1] * nums[i], nums[i]));
		}
		return *max_element(dp_max.begin(), dp_max.end());
	}
};

时间复杂度:O(n)

空间复杂度:O(n)

 

改进:

上方代码中,我们使用数组 dp_max 和 dp_min 分别存放最大子数组的最大乘积和最小乘积。

由于题目只需要最终结果,我们可以使用 变量 来进行存储和更新,可以将空间复杂度降低至 O(1)

改进代码如下:

class Solution {
public:
	int maxProduct(vector<int>& nums) {
		int len = nums.size();
		int dp_max, dp_min, ans;
		dp_max = nums[0], dp_min = nums[0], ans = nums[0];
		for (int i = 1; i < len; i++) {
			dp_max = max(dp_max * nums[i], max(dp_min * nums[i], nums[i]));
			dp_min = min(dp_max * nums[i], min(dp_min * nums[i], nums[i]));
			ans = max(ans, dp_max);
		}
		return ans;
	}
};

 

参考:乘积最大子数组

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值