Loading

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);
}    
posted @ 2021-02-02 23:47  反身而诚、  阅读(39)  评论(0编辑  收藏  举报