https://www.lintcode.com/problem/maximum-product-subarray/
求最值,用动态规划解题。
解题步骤:
1. 确定状态: (但凡动规,都要开辟数组存下当前状态。递归则不需要存状态,因为它每次递归都会重新计算,所以运算量远远大于动规解法)因为要求乘积最大,可以开个一维数组res[], res[i]保存当前数组nums[0....i]从下标0到i的乘积最大值
- 最后一步
res[n-1]保存的是最后一步计算的最终结果
- 子问题
不考虑最后一个值nums[n-1],子问题变成求nums[0,....n-2]连续子序列的乘积最大
2. 转移方程:
考虑最后一步res[n-1]和res[n-2]之间的关系如何?
最后一步是不是要考虑算上nums[n-1]的时候,就是从nums[0...n-1]的连续最大乘积,与之前res[n-2]结果比较,取较大的值:
res[n-1] = max{ res[n-2] , 连续序列nums[0...n-1]乘积最大值 }
3. 注意边界值和条件:
数组下标是从0 ---- n-1,
初始值:res[0] = nums[0],
连续序列nums[0...n-1]乘积最大值: 可以让maxValue存,从nums[n-1]乘到nums[0]之间的最大值。(这步骤可以考虑优化)
4. 计算顺序:
由转移方程可以知道,要想知道res[n-1],必须先计算res[n-2],所以顺序从res[0]开始算到res[n-1]。 最终返回res[n-1]的值即可。
写代码求解:
public class Solution {
/**
* @param nums: An array of integers
* @return: An integer
*/
public int maxProduct(int[] nums) {
// write your code here
int len = nums.length;
int[] mult = new int[len];
mult[0] = nums[0]; //初始化
for(int i = 1; i < len; i++){
int maxValue = nums[i]; //包含nums[i]的乘积最大子序列
int multRes = nums[i];
for(int j=i-1;j>=0;j--){
multRes *= nums[j];
if(multRes>maxValue){
maxValue = multRes;
}
}
mult[i] = Math.max(mult[i-1],maxValue);
}
return mult[len-1];
}
}
可以看到我这种解法,因为求包含nums[i]的最大子序列乘积,
用一个for循环求解还是挺耗时的,感觉可以从这边入手考虑优化。
我这个解法耗时O(n^2)
各位可以想想怎么优化吧
这个解法耗时O(n),
开辟了两个数组,分别存当前乘积最大值和最小值,来应对负数情况,思路挺不错的:
max[i]数组,存的是包含当前nums[i]的序列最大乘积,
min[i]数组,保存的是当前nums[i]的序列最小乘积,
用result来保存当前子序列的最大乘积值。
当nums[i]==0时,考虑,因为0乘任何数都是0。
转移方程:
min[i] = max[i] = nums[i];
if (nums[i] > 0) {
max[i] = Math.max(max[i], max[i - 1] * nums[i]);
min[i] = Math.min(min[i], min[i - 1] * nums[i]);
} else if (nums[i] < 0) {
max[i] = Math.max(max[i], min[i - 1] * nums[i]);
min[i] = Math.min(min[i], max[i - 1] * nums[i]);
}
举个例子:
num[2,3,-2, -5, -4, 2, 0,3,-4, 5, 2, -1, -1] ---->得出结果是120
min[2,3,-12,-5,-240,-480,0,3,-12,-60,-120,-10,-1]
max[2,6,-2, 60, 20, 40, 0,3,-4, 5, 10, 120,10]
result(2, 6, 6, 60, 60, 60, 60, 60, 60, 60, 60, 120, 120)---->得出结果是120