2020.11.7
原题:点击此处
本道题很明显是一道动态规划的题目。
直接在题目中给出“最大”的词眼。
不过做了两次我还是没有做出来。。
第一次的想法是用一个二维数组去不断根据上一行遍历,但是这样的话会因为出现0,导致代码非常臃肿且易错。
第二次看了题解之后还是错了。
本道题的原处思路是分类讨论,并且分类讨论的情况还比较多的。
首先,我们要建立的DP数组为dpMax[i]
它的含义是 以i
为结尾的最大序列和乘积是多少,所以必定是包含nums[i]
的
以下我们进行分类讨论。
1、当nums[i] >= 0
,且dpMax[i-1] >= 0
时,dpMax[i] = nums[i] * dpMax[i-1]
2、当nums[i] >= 0
且dpMax[i-1]<0
时,考虑到相乘之后只会越来越小,所以dpMax[i] = nums[i]
3、当nums[i] < 0
时,这个条件是让本题变得困难的罪魁祸首。
我们需要得知,在这种情况下,要得到一个最大的数,我们需要前面极可能小的负数相乘,就会得到一个最大的数,因此我们需要额外一个数组dpMin
来存储以i
结尾的最小序列乘积和是多少。
当dpMin[i-1] < 0
时,dpMax[i] = dpMin[i-1] * nums[i]
;
当dpMin[i-1] >= 0
时,dpMax[i] = nums[i]
4、根据上面的条件,我们知道,如果要写代码进行模拟,将会出现非常多的if和else语句,因此使用技巧观察dpMax
,可以得知,无论如何,dpMax
都只会在三个值里面取,因此每次都只要比较三个值的大小即可,并且我们可以知道dpMax
和dpMin
都是只跟前一个数有关,因此可以不用数组,只用常数就可以进行记录。
代码如下:
class Solution {
public int maxProduct(int[] nums) {
int imax = 1;
int imin = 1;
int max = nums[0];
for(int i = 0; i<nums.length; i++){
int preMax = imax;
imax = Math.max(nums[i],Math.max(imax*nums[i],imin*nums[i]));
//System.out.println(imax);
imin = Math.min(nums[i],Math.min(preMax*nums[i],imin*nums[i]));
//System.out.println(imin);
max = Math.max(imax,max);
}
return max;
}
}
易错点:
1、忘记写preMax,导致imin的计算出错。
2、max的比较是imax和max,不应该是preMax和imax。
3、imax和imin的初始值应该是1.