题目链接:力扣
本题显然也是动态规划的思路,假设Max[i]是以i为结尾的子数组的最大乘积,即Max[i]=max(nums[j]*```*nums[i]),其中0<=j<i<nums.size()。则显然,Max[i]与Max[i-1]有关。首先考虑
Max[i]=max(nums[i],Max[i-1]*nums[i])
这与“和最大子数组”的动态规划转移方程相似,而放在乘积最大的情形下并不适用。因为如果求和最大,那么Max[i]有以下几种情况:
1. nums[i]>0, Max[i-1]>0 --> Max[i]=Max[i-1]+nums[i]=max(nums[i],Max[i-1]+nums[i])
2. nums[i]<=0, Max[i-1]>0 --> Max[i]=Max[i-1]+nums[i]=max(nums[i],Max[i-1]+nums[i])
3. nums[i]>0, Max[i-1]<=0 --> Max[i]=nums[i]=max(nums[i],Max[i-1]+nums[i])
4. nums[i]<=0, Max[i-1]<=0 --> Max[i]=nums[i]=max(nums[i],Max[i-1]+nums[i])
因此可以整理出Max[i]=max(nums[i],Max[i-1]+nums[i])。
而对于乘积的情况,若出现了负数,就显然不能套用上面的公式。举个例子:
nums={1,2,-3,-6}
显然
Max={1,2,-3,36}
而若用Max[i]=max(nums[i],Max[i-1]*nums[i])来计算,就会出现
Max={1,2,-3,18}
很显然,在num[i]<0的情况下,我们不应该考虑Max[i-1],而应该考虑Min[i-1],即我们希望乘一个绝对值最大的负数,这样负负得正,才能得到最合适的Max[i];而对于nums[i]>0的情况,就希望找到一个绝对值最大的正数,相乘才能得到最合适的Max[i]。因此需要多维护一个Min数组。状态转移方程如下:
Max[i]=max(nums[i]*Max[i-1],nums[i]*Min[i-1],nums[i])
Min[i]=min(nums[i]*Max[i-1],nums[i]*Min[i-1],nums[i])
代码如下:
class Solution {
public:
int Max[20010],Min[20010];
void init(vector<int>& nums){
Max[0]=Min[0]=nums[0];
for(int i=1;i<nums.size();i++){
Max[i]=max(nums[i],max(nums[i]*Max[i-1],nums[i]*Min[i-1]));
Min[i]=min(nums[i],min(nums[i]*Max[i-1],nums[i]*Min[i-1]));
}
}
int maxProduct(vector<int>& nums) {
init(nums);
int max=Max[0];
for(int i=1;i<nums.size();i++){
if(Max[i]>max) max=Max[i];
}
return max;
}
};
时间复杂度: O(n)
空间复杂度: O(n)