LeetCode 152. Maximum Product Subarray (最大乘积子数组)

Given an integer array nums, find the contiguous subarray within an array (containing at least one number) which has the largest product.

Example 1:

Input: [2,3,-2,4]
Output: 6
Explanation: [2,3] has the largest product 6.

Example 2:

Input: [-2,0,-1]
Output: 0
Explanation: The result cannot be 2, because [-2,-1] is not a subarray.

Reference Answer

思路分析

这个求最大子数组乘积问题是由最大子数组之和问题演变而来,但是却比求最大子数组之和要复杂,因为在求和的时候,遇到0,不会改变最大值,遇到负数,也只是会减小最大值而已。而在求最大子数组乘积的问题中,遇到0会使整个乘积为0,而遇到负数,则会使最大乘积变成最小乘积,正因为有负数和0的存在,使问题变得复杂了不少。。

比如,我们现在有一个数组[2, 3, -2, 4],我们可以很容易的找出所有的连续子数组,[2], [3], [-2], [4], [2, 3], [3, -2], [-2, 4], [2, 3, -2], [3, -2, 4], [2, 3, -2, 4], 然后可以很轻松的算出最大的子数组乘积为6,来自子数组[2, 3].

那么我们如何写代码来实现自动找出最大子数组乘积呢,我最先想到的方比较简单粗暴,就是找出所有的子数组,然后算出每一个子数组的乘积,然后比较找出最大的一个,需要两个for循环,第一个for遍历整个数组,第二个for遍历含有当前数字的子数组,就是按以下顺序找出子数组: [2], [2, 3], [2, 3, -2], [2, 3, -2, 4], [3], [3, -2], [3, -2, 4], [-2], [-2, 4], [4], 我在本地测试的一些数组全部通过,于是兴高采烈的拿到OJ上测试,结果丧心病狂的OJ用一个有15000个数字的数组来测试,然后说我程序的运行时间超过了要求值,我一看我的代码,果然如此,时间复杂度O(n2), 得想办法只用一次循环搞定。我想来想去想不出好方法,于是到网上搜各位大神的解决方法。其实这道题最直接的方法就是用DP来做,而且要用两个dp数组,其中f[i]表示子数组[0, i]范围内的最大子数组乘积,g[i]表示子数组[0, i]范围内的最小子数组乘积,初始化时f[0]和g[0]都初始化为nums[0],其余都初始化为0。那么从数组的第二个数字开始遍历,那么此时的最大值和最小值只会在这三个数字之间产生,即f[i-1]*nums[i],g[i-1]*nums[i],和nums[i]。所以我们用三者中的最大值来更新f[i],用最小值来更新g[i],然后用f[i]来更新结果res即可。

Code

class Solution:
    def maxProduct(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        if len(nums) == 1:
            return nums[0]
        res = max_temp = min_temp = nums[0]
        
        for count in nums[1:]:
            pre_max, pre_min = max_temp, min_temp
            max_temp = max(pre_max*count, count, pre_min*count)
            min_temp = min(pre_max*count, count, pre_min*count)
            res = max(res, max_temp)
        return res
                    

C++ Version

class Solution {
public:
    int maxProduct(vector<int>& nums) {
        if (nums.size() == 1){
            return nums[0];
        }
        int res = nums[0], min_temp = nums[0], max_temp = nums[0];
        for (int i=1; i<nums.size(); ++i){
            int pre_max = max_temp;
            int pre_min = min_temp;
            max_temp = max(max(pre_max*nums[i], nums[i]), pre_min*nums[i]);
            min_temp = min(min(pre_max*nums[i], nums[i]), pre_min*nums[i]);
            res = max(max_temp, res);
                
        }
        return res;
    }
};

Note

  • 这种应多复杂情况采用采用多个dp数组记录状态的做法很值得学习,后面很多DP问题都是需要借鉴这种多个dp数组记录状态应对复杂场景约束的动态规划问题!

参考文献

[1] http://www.cnblogs.com/grandyang/p/4028713.html

猜你喜欢

转载自blog.csdn.net/Dby_freedom/article/details/84891875
今日推荐