这道题其实和最大子数组和很像,但是稍微有点麻烦,其实也就多了那么一个值而已。都是动态规划的状态方程问题。
思路:首先我们可以在前面的最大子数组和中知道,可以用f(n)=max(f(n-1)*a[n],a[n])的状态方程来实现初步的判断,然后再重新判断可能有负数的情况,res=max(res,f(i))就是这种保险。这里的思路与此类似。
这里我们需要知道,如果说一个数组里面的数全都是正数的话,我们很快就能想到,这里直接可以用上面的那个状态方程改成乘法就行了,甚至不用判断负数的情况直接出答案。但是呢,这里是有正数负数相牵连的。我们知道,如果说一个正数乘以负数的话,这个数就会变成负数,会越乘积越小。那么重点就来了:
假如我们开始遍历数组,
1.当前我们遍历到的是一个正数,我们希望前面的乘积是正的才是最优解,对吧?这是母庸质疑的。可是前面的乘积又不一定是正数,所以我们只能拿着当前位置与它前面的乘积相乘之后的结果在进行比较,这里的乘积结果也并不是最终的,我们还需要知道,在当前位置时,如今的最小值是多少,最小值也可能是正数,也可能是负数;这个时候我们如果说这个最小值再乘以当前的正数,也可能变成最大值了,因为如果前面的数是负数与当前位置元素相乘的话,也不一定就是最小值最大值的事了,所以我们需要额外判断。这样,第一个方程就能写出来了:
F(n)max=max(F(n-1)*a[n],a[n],F(n-1)min*a[n]).
那么同理,当我们知道当前是负数之后,也就可以类似的这样判断了,我们现在是负数,我们也希望前面的乘积也是负数才对,这样才会乘积出来最大值。所以我们也需要判断这样的情况,就如同上面的方程样。
有人会问,为什么我们要知道最小值呢?其实,如果说我们知道了前面的最小值,且最小值是负数,如果说当前位置是负数,那么这个乘积不就成为正数了吗?这个时候最大值是否还是最大值我们就不确定了,所以需要知道最小值是多少,是因为考虑到负数的情况,这一点和最大子数组和那道题是截然不同,但是也有相似之处,也就是都需要考虑负数的情况。
F(n)min=min(F(n-1)*a[n],a[n],F(n-1)max*a[n])
这样这两个状态方程就这样写完了。
另外需要提醒大家,牛客网上的一道题DP7是一样的题目,课下用来巩固重新写一遍就行了。
接下来我们就直接上代码:
class Solution {
public:
int maxProduct(vector<int>& nums) {
int n=nums.size();
int i;
if(n==0)
return 0;
else if(n==1)
return nums[0];
else{
int maxH=nums[0];
int minH=nums[0];
int res=nums[0];
for(int i=1;i<n;i++){
int t=maxH;
maxH=max(max(maxH*nums[i],nums[i]),minH*nums[i]);
minH=min(min(t*nums[i],nums[i]),minH*nums[i]);
res=max(res,maxH);
}
return res;
}
}
};