题目及测试
package pid152;
/* 乘积最大子序列
给定一个整数数组 nums ,找出一个序列中乘积最大的连续子序列(该序列至少包含一个数)。
示例 1:
输入: [2,3,-2,4]
输出: 6
解释: 子数组 [2,3] 有最大乘积 6。
示例 2:
输入: [-2,0,-1]
输出: 0
解释: 结果不能为 2, 因为 [-2,-1] 不是子数组。
}*/
public class main {
public static void main(String[] args) {
int[][] testTable = {{2,3,-2,4},{-2,0,-1},{1,2,3,5},{1,2,-1,-2,2,1,-2,1,4,-5,4}};
for (int[] ito : testTable) {
test(ito);
}
}
private static void test(int[] ito) {
Solution solution = new Solution();
int rtn;
long begin = System.currentTimeMillis();
for (int i = 0; i < ito.length; i++) {
System.out.print(ito[i]+" ");
}//开始时打印数组
rtn = solution.maxProduct(ito);//执行程序
long end = System.currentTimeMillis();
System.out.println("rtn=" + rtn);
/*for (int i = 0; i < rtn; i++) {
System.out.print(ito[i]+" ");
}//打印结果几数组
*/ System.out.println();
System.out.println("耗时:" + (end - begin) + "ms");
System.out.println("-------------------");
}
}
解法1(成功,2ms,极快)
设定max数组和min数组,分别代表以i为结尾的乘积最大/小子序列之积
每次计算新的max[i],min[i],根据nums[i],max[i-1],min[i-1],三者小于0,大于0,等于0,来设置。
package pid152;
import java.util.Arrays;
public class Solution {
public int maxProduct(int[] nums) {
int length=nums.length;
if(length==0){
return 0;
}
if(length==1){
return nums[0];
}
// 以i为结尾的乘积最大子序列之积
int[] max=new int[length];
// 以i为结尾的乘积最小子序列之积
int[] min=new int[length];
max[0]=nums[0];
min[0]=nums[0];
for(int i=1;i<length;i++){
int now=nums[i];
int prevMax=max[i-1];
int prevMin=min[i-1];
if(now==0){
max[i]=0;
min[i]=0;
continue;
}
if(now>0){
// 当前数为正
// 设置max
// 如果prevMax为0,max=now
if(prevMax<=0){
max[i]=now;
}else{
max[i]=now*prevMax;
}
// 设置min
// 如果prevMin为0,max=now*0
if(prevMin>0){
min[i]=now;
}else{
min[i]=now*prevMin;
}
}else{
// 当前数为负数
// 设置max
// 如果prevMin为0,max为now*0
if(prevMin>0){
max[i]=now;
}else{
max[i]=now*prevMin;
}
// 设置min
// 如果prevMax为0,min为now
if(prevMax<=0){
min[i]=now;
}else{
min[i]=now*prevMax;
}
}
}
int totalMax=max[0];
for(int i=1;i<length;i++){
if(totalMax<max[i]){
totalMax=max[i];
}
}
return totalMax;
}
}
解法2(2ms,极快,成功)
我们注意到上边max[i] 的取值无非就是三种,max[i-1] * nums[i]、min[i-1] * nums[i] 以及 nums[i]。
所以我们更新的时候,无需去区分当前是哪种情况,只需要从三个取值中选一个最大的即可。
max[i] = max(max[i-1] * nums[i], min[i-1] * nums[i], nums[i]);
求 dpMin[i] 同理。
min[i] = min(max[i-1] * nums[i], min[i-1] * nums[i], nums[i]);
更新过程中,我们可以用一个变量 max 去保存当前得到的最大值。
动态规划的老问题,我们注意到更新 dp[i] 的时候,我们只用到 dp[i-1] 的信息,再之前的信息就用不到了。所以我们完全不需要一个数组,只需要一个变量去重复覆盖更新即可。
public int maxProduct(int[] nums) {
int length=nums.length;
if(length==0){
return 0;
}
if(length==1){
return nums[0];
}
int totalMax = nums[0];
int prevMax = nums[0];
int prevMin = nums[0];
int now = 0;
int prevMaxMulti = 0;
int prevMinMulti = 0;
for(int i=1;i<length;i++) {
now = nums[i];
prevMaxMulti = prevMax * now;
prevMinMulti = prevMin * now;
prevMax = Math.max(Math.max(prevMaxMulti, prevMinMulti), now);
prevMin = Math.min(Math.min(prevMaxMulti, prevMinMulti), now);
totalMax = Math.max(totalMax, prevMax);
}
return totalMax;
}