力扣HOT100之动态规划:152. 乘积最大子数组
这道题并不是代码随想录里的,我试着用动规五部曲来做,然后不能通过全部测试样例,在第109个测试样例卡住了,如下所示。
原因是可能负数乘以负数会得到最大的乘积,不能单纯地用上一个序列的最大值乘以当前值来判断是否能得到更大值。后来结合了一下灵神的题解,改良了一下动规五部曲,我们同时维护max_multiply
和min_multiply
两个数组,他们第i
个位置上的元素的含义为:以nums[i]
结尾的数组中所能取到的最大非空连续子数组乘积为max_multiply[i]
,以nums[i]
结尾的数组中所能取到的最小非空连续子数组乘积为min_multiply[i]
。而dp[i]
的含义为:以nums[i]
为结尾的情况下,所能取到的非空连续子数组的最大乘积。我们可以得到如下4种情况:
max_multiply[i - 1]
为正,nums[i]
为正 / 0,无论min_multiply[i - 1]
为何值,此时dp[i] = max_multiply[i - 1] * nums[i]
max_multiply[i - 1]
为负,nums[i]
为正 / 0,min_multiply[i - 1]
只能为负,此时dp[i] = nums[i]
max_multiply[i - 1]
为正,nums[i]
为负,无论min_multiply[i - 1]
为何值,此时dp[i] = max(nums[i], nums[i] * min_multiply[i - 1])
max_multiply[i - 1]
为负,nums[i]
为负,min_multiply[i - 1]
只能为负,此时dp[i] = nums[i] * min_multiply[i - 1])
综上,dp[i]
一定会在{nums[i], max_multiply[i - 1] * nums[i], min_multiply[i - 1] * nums[i]}
中产生,因此我们每一次更新dp[i]
时,在三者中取最大值即可。下面给出动规五部曲:
1.确定dp[i]
的含义:以nums[i]
为结尾的情况下,所能取到的非空连续子数组的最大乘积
2.确定递推公式dp[i] = max_multiply[i];
3.dp数组初始化dp[0] = nums[0]
4.确定遍历顺序:从左往右遍历
5.打印数组(省略)
同样,最大乘积不一定是以最后一个元素结尾构成的连续子数组产生的,我们同样用一个变量result
来维护最大乘积。
class Solution {
public:int maxProduct(vector<int>& nums) {//1.确定dp[i]的含义:以nums[i]为结尾的情况下,所能取到的非空连续子数组的最大乘积//2.确定递推公式 dp[i] = max(nums[i], nums[i] * dp[i - 1]);//3.dp数组初始化 dp[0] = nums[0] //4.确定遍历顺序:从左往右遍历//5.打印数组(省略)int n = nums.size();vector<int> dp(n);vector<int> max_multiply(n);vector<int> min_multiply(n);//初始化dp[0] = nums[0];max_multiply[0] = nums[0];min_multiply[0] = nums[0];int result = nums[0];for(int i = 1; i < n; i++){// for(int j = 0; j < i; j++){max_multiply[i] = max({nums[i], nums[i] * max_multiply[i - 1], nums[i] * min_multiply[i - 1]});min_multiply[i] = min({nums[i], nums[i] * max_multiply[i - 1], nums[i] * min_multiply[i - 1]});dp[i] = max_multiply[i];result = max(result, dp[i]);}return result;}
};