LeetCode-152-乘积最大子数组

LeetCode-152-乘积最大子数组

题目

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

示例 1:

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

示例 2:

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

思路

这道题乍一看很简单,和之前最最大子数组之和一样,我很轻松的列出了状态转移方程:

\[dp[i]=max(dp[i-1]*x,x); \]

提交之后发现在自己错的离谱,问题就出在正数和负数上,这里的负数与负数相乘会得到更大的正数;

也就是说如果在前一个点保存的最大正数,当后面为负数的时候,正数反而没有用了,需要负数来相乘;

一开始我没有意识到问题的复杂性,自己做了一堆if语句来尝试解决这个问题,结果只能通过前面的简单的用例;

后面复杂的就不能通过了;

实在没有办法了,写了一个O(N^2)的暴力代码,结果当输入数据过大的时候超时了;

万般无奈就去看了题解,发现我就是题解中说明的典型错误示范。。。题解的正确思路是这样的:

我们可以根据正负性进行分类讨论。

考虑当前位置如果是一个负数的话,那么我们希望以它前一个位置结尾的某个段的积也是个负数,这样就可以负负得正,并且我们希望这个积尽可能「负得更多」,即尽可能小。如果当前位置是一个正数的话,我们更希望以它前一个位置结尾的某个段的积也是个正数,并且希望它尽可能地大。于是这里我们可以再维护一个 fmin⁡(i),它表示以第 i 个元素结尾的乘积最小子数组的乘积,那么我们可以得到这样的动态规划转移方程:

\[\begin{array}{l}{f_{\max }}(i) = \mathop {\max }\limits_{i = 1}^n \{ {f_{\max }}(i - 1) \times {a_i},{f_{\min }}(i - 1) \times {a_i},ai\} \\{f_{\min }}(i) = \mathop {\max }\limits_{i = 1}^n \{ {f_{\max }}(i - 1) \times {a_i},{f_{\min }}(i - 1) \times {a_i},ai\} \end{array} \]

看了人家的思路,顺便就看到了代码,果然还是官方的思路厉害,感觉自己还是太菜了。。

代码

class Solution {
public:
    int maxProduct(vector<int>& nums) {
        vector <int> maxF(nums), minF(nums);
        for (int i = 1; i < nums.size(); ++i) {
            maxF[i] = max(maxF[i - 1] * nums[i], max(nums[i], minF[i - 1] * nums[i]));
            minF[i] = min(minF[i - 1] * nums[i], min(nums[i], maxF[i - 1] * nums[i]));
        }
        return *max_element(maxF.begin(), maxF.end());
    }
};

posted @ 2020-06-06 19:55  樱花小猪  阅读(144)  评论(0编辑  收藏  举报