Medium | LeetCode 152. 乘积最大子数组 | 动态规划
152. 乘积最大子数组
给你一个整数数组 nums
,请你找出数组中乘积最大的连续子数组(该子数组中至少包含一个数字),并返回该子数组所对应的乘积。
示例 1:
输入: [2,3,-2,4]
输出: 6
解释: 子数组 [2,3] 有最大乘积 6。
示例 2:
输入: [-2,0,-1]
输出: 0
解释: 结果不能为 2, 因为 [-2,-1] 不是子数组。
解题思路
此题的问题在于数组中存在负数的情况。负数让普通的动态规划没法处理。
解决办法是维护两个动态规划数组。类似于 LeetCode 309. 最佳买卖股票时机含冷冻期 | 动态规划(状态机) 维护了三个数组。
考虑当前位置如果是一个负数的话,那么我们希望以它前一个位置结尾的某个段的积也是个负数,这样就可以负负得正,并且我们希望这个积尽可能「负得更多」,即尽可能小。如果当前位置是一个正数的话,我们更希望以它前一个位置结尾的某个段的积也是个正数,并且希望它尽可能地大。于是这里我们可以再维护一个
${f_{\min }}(i) $,它表示以第 ii 个元素结尾的乘积最小子数组的乘积,那么我们可以得到这样的动态规划转移方程:
\[\begin{array}{l}
f_{\max }(i)=\max _{\imath=1}^{n}\left\{f_{\max }(i-1) \times a_{i}, f_{\min }(i-1) \times a_{i}, a_{i}\right\} \\
f_{\min }(i)=\min _{\imath=1}\left\{f_{\max }(i-1) \times a_{i}, f_{\min }(i-1) \times a_{i}, a_{i}\right\}
\end{array}
\]
public int maxProduct(int[] nums) {
int[] maxDp = new int[nums.length];
int[] minDp = new int[nums.length];
maxDp[0] = minDp[0] = nums[0];
for (int i = 1; i < nums.length; i++) {
maxDp[i] = maxInThree(nums[i], nums[i] * maxDp[i-1], nums[i] * minDp[i-1]);
minDp[i] = minInThree(nums[i], nums[i] * maxDp[i-1], nums[i] * minDp[i-1]);
}
int ans = maxDp[0];
for (int i = 0; i < maxDp.length; i++) {
ans = Math.max(ans, maxDp[i]);
}
return ans;
}
public int maxInThree(int a, int b, int c) {
return Math.max(Math.max(a, b), c);
}
public int minInThree(int a, int b, int c) {
return Math.min(Math.min(a, b), c);
}