1.动态规划数组
class Solution {
public int maxProduct(int[] nums) {
int[][] dp = new int[nums.length][2];
dp[0][0]=nums[0];
dp[0][1]=nums[0];
for(int i=1;i<nums.length;i++){
if(nums[i]>=0){
dp[i][0]=Math.max(nums[i],dp[i-1][0]*nums[i]);
dp[i][1]=Math.min(nums[i],dp[i-1][1]*nums[i]);
}else{
dp[i][0]=Math.max(nums[i],dp[i-1][1]*nums[i]);
dp[i][1]=Math.min(nums[i],dp[i-1][0]*nums[i]);
}
}
int max=dp[0][0];
for(int i=1;i<nums.length;i++){
max=Math.max(max,dp[i][0]);
}
return max;
}
}
dp[i][j] 代表的意思是 必须以nums[i]结尾的子数组的值,j只有两个值0和1
dp[i][0]代表以nums[i]结尾的子数组的最大值
dp[i][1]代表以nums[i]结尾的子数组的最小值
这个题之所以不能像之前的求连续数组的最大和,就是因为 dp[i][j]和最大值最小值有关系,
正数和负数相乘后,可能直接导致最大值和最小值之间的转换,故期间有状态转移关系
接下来,就是推导状态转移方程
这里要分为正数和负数两种情况
如果nums[i]>=0 dp[i][0]=Math.max(nums[i],dp[i-1][0]*nums[i]);
dp[i][1]=Math.min(nums[i],dp[i-1][1]*nums[i]);如果nums[i-1]为正数,自然是越乘越大,如果nums[i-1]为负数,自然是选择nums[i]最大
如果nums[i-1]为负数,自然是越乘越这个数越负,越小,如果nums[i-1]为正数,自然是选择nums[i]最小
如果nums[i]<0 dp[i][0]=Math.max(nums[i],dp[i-1][1]*nums[i]);
dp[i][1]=Math.min(nums[i],dp[i-1][0]*nums[i]);如果nums[i-1]为正数,自然是越乘越这个数越负,越小,选择nums[i],如果nums[i-1]为负数,自然是越乘越大,负负得正
如果nums[i-1]为负数,自然是越乘越大,选择nums[i],如果nums[i-1]为正数,自然是选择相乘最小
2.优化
class Solution {
public int maxProduct(int[] nums) {
int preMax=nums[0],preMin=nums[0],max=nums[0],temp;
for(int i=1;i<nums.length;i++){
if(nums[i]>=0){
preMax=Math.max(nums[i],preMax*nums[i]);
preMin=Math.min(nums[i],preMin*nums[i]);
}else{
temp=Math.max(nums[i],preMin*nums[i]);
preMin=Math.min(nums[i],preMax*nums[i]);
preMax=temp;
}
max=Math.max(max,preMax);
}
return max;
}
}
认真观察没有优化之前的代码,可以看出来,主要变量集中在 dp[i-1][0],dp[i-1][1],几乎没有用到动态方程中的其他变量,除了最后遍历的时候需要遍历取最大值,但是其实这个循环也可以放在第一个循环进行,
用preMax,preMin来代替dp[i-1][0],dp[i-1][1],在每次得到preMax后与max比大小,去最大值
除了nums[i]<0处,preMax需要一个temp临时变量先存储一下值