leetcode-乘积最大子数组

206 阅读2分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

早上早起半小时,好处很多,比如上班前可以做一些leetcode,争取明天继续吧。今天继续做leetcode的动态规划的分组。

题目

给你一个整数数组 nums ,请你找出数组中乘积最大的连续子数组(该子数组中至少包含一个数字),并返回该子数组所对应的乘积。

示例 1:
输入: [2,3,-2,4]
输出: 6
解释: 子数组 [2,3] 有最大乘积 6。

示例 2:
输入: [-2,0,-1]
输出: 0
解释: 结果不能为 2, 因为 [-2,-1] 不是子数组。

思路

这些题目,其实都是在最大最序列和的基础上,出现的一些变种,昨天做的“环形子数组的最大和”和前天做的“打家劫舍”,都是差不多的类型。不难想到这类题目的状态定义就是数组dp[],dp[i]的含义是,在nums[0]~nums[i]数组中,以nums[i]结尾可以得到的最大乘积。按照最大子序列和的做法,比较容易想到的是 dp[i] = max(dp[i-1]*nums[i], nums[i])。但是这里可能存在负数,那上面这个状态转义方程就不成立了。继续思考下去,如果num[i]本身是负数的情况下,希望dp[i-1]也是负数,而且绝对值越大越好,这样负负得正后,得到的结果也越大,所以不止需要维护一个最大值的数组dp[] (改成max[]),还要位置一个最小值的数组min[],有如下的状态转移方程

  • max[i] = max(nums[i], nums[i]*max[i-1], nums[i]*min[i-1])
  • min[i] = min(nums[i], nums[i]*max[i-1], nums[i]*min[i-1]) 考虑到max[i]只跟max[i-1]相关,min[i]只跟min[i-1]相关,可以把这2个数组优化成2个变量,降低空间复杂度。

Java版本代码

class Solution {
    public int maxProduct(int[] nums) {
        int ans = nums[0];
        int maxPre = nums[0];
        int minPre = nums[0];
        for (int i = 1; i < nums.length; i++) {
            int maxTemp = Math.max(nums[i], Math.max(nums[i]*maxPre, nums[i]*minPre));
            int minTemp = Math.min(nums[i], Math.min(nums[i]*maxPre, nums[i]*minPre));
            if (maxTemp > ans) {
                ans = maxTemp;
            }
            maxPre = maxTemp;
            minPre = minTemp;
        }
        return ans;
    }
}