LeetCode-152-乘积最大子数组
LeetCode-152-乘积最大子数组
题目
给你一个整数数组 nums
,请你找出数组中乘积最大的连续子数组(该子数组中至少包含一个数字),并返回该子数组所对应的乘积。
示例 1:
输入: [2,3,-2,4]
输出: 6
解释: 子数组 [2,3] 有最大乘积 6。
示例 2:
输入: [-2,0,-1]
输出: 0
解释: 结果不能为 2, 因为 [-2,-1] 不是子数组。
思路
这道题乍一看很简单,和之前最最大子数组之和一样,我很轻松的列出了状态转移方程:
\[dp[i]=max(dp[i-1]*x,x);
\]
提交之后发现在自己错的离谱,问题就出在正数和负数上,这里的负数与负数相乘会得到更大的正数;
也就是说如果在前一个点保存的最大正数,当后面为负数的时候,正数反而没有用了,需要负数来相乘;
一开始我没有意识到问题的复杂性,自己做了一堆if语句来尝试解决这个问题,结果只能通过前面的简单的用例;
后面复杂的就不能通过了;
实在没有办法了,写了一个O(N^2)的暴力代码,结果当输入数据过大的时候超时了;
万般无奈就去看了题解,发现我就是题解中说明的典型错误示范。。。题解的正确思路是这样的:
我们可以根据正负性进行分类讨论。
考虑当前位置如果是一个负数的话,那么我们希望以它前一个位置结尾的某个段的积也是个负数,这样就可以负负得正,并且我们希望这个积尽可能「负得更多」,即尽可能小。如果当前位置是一个正数的话,我们更希望以它前一个位置结尾的某个段的积也是个正数,并且希望它尽可能地大。于是这里我们可以再维护一个 fmin(i),它表示以第 i 个元素结尾的乘积最小子数组的乘积,那么我们可以得到这样的动态规划转移方程:
\[\begin{array}{l}{f_{\max }}(i) = \mathop {\max }\limits_{i = 1}^n \{ {f_{\max }}(i - 1) \times {a_i},{f_{\min }}(i - 1) \times {a_i},ai\} \\{f_{\min }}(i) = \mathop {\max }\limits_{i = 1}^n \{ {f_{\max }}(i - 1) \times {a_i},{f_{\min }}(i - 1) \times {a_i},ai\} \end{array}
\]
看了人家的思路,顺便就看到了代码,果然还是官方的思路厉害,感觉自己还是太菜了。。
代码
class Solution {
public:
int maxProduct(vector<int>& nums) {
vector <int> maxF(nums), minF(nums);
for (int i = 1; i < nums.size(); ++i) {
maxF[i] = max(maxF[i - 1] * nums[i], max(nums[i], minF[i - 1] * nums[i]));
minF[i] = min(minF[i - 1] * nums[i], min(nums[i], maxF[i - 1] * nums[i]));
}
return *max_element(maxF.begin(), maxF.end());
}
};