LintCode 191: Maximum Product Subarray (DP 经典题!)

  1. Maximum Product Subarray

Find the contiguous subarray within an array (containing at least one number) which has the largest product.

Example
Example 1:

Input:[2,3,-2,4]
Output:6
Example 2:

Input:[-1,2,4,1]
Output:8

解法1:
思路: DP。因为数组中可能有正有负或0。用两个数组dpPosMax[]和dpNegMin[] 分别存储正的最大值和负的最小值。

  1. 当nums[0]>0时,dpPosMax[0]=nums[0], dpNegMin[0]=0,当nums[0]<0时,dpNegMin[0]=nums[0], dpPosMax[0]=0。
  2. 遍历数组,当nums[i]>0时,
    更新dpPosMax[i]: 因为dpPosMax[i]可能为正或0,当为正时,更新为dpPosMax[i - 1] * nums[i] ,因为nums[i]>=1,所以乘积肯定>=dpPosMax[i-1]; 当为0时,更新为nums[i]即可。
    更新dpNegMin[i]: 因为dpNegMin[i]可能为负或0,当为负时,更新为dpNegMin[i-1]*nums[i],因为nums[i]>=1,所以乘积肯定更小 ;当为0时,更新为nums[i]即可。
    dpPosMax[i] = dpPosMax[i - 1] > 0 ? dpPosMax[i - 1] * nums[i] : nums[i];
    dpNegMin[i] = dpNegMin[i -1] < 0 ? dpNegMin[i - 1] * nums[i] : 0;
  3. 对于nums[i]<0的情形类似2)
  4. 当nums[i]==0时,dpPosMax[i]和dpNegMin[i]都为0。因为已经初始化为0了,所以不用更新。
  5. 循环中动态更新result。
    代码如下:
class Solution {
public:
    /**
     * @param nums: An array of integers
     * @return: An integer
     */
    int maxProduct(vector<int> &nums) {
        int len = nums.size();
        if (len == 0) return 0;
        
        vector<int> dpPosMax(len, 0);
        vector<int> dpNegMin(len, 0);
        int result = nums[0];
        
        if (nums[0] > 0) {
            dpPosMax[0] = nums[0];
        }

        if (nums[0] < 0) {
            dpNegMin[0] = nums[0];
        }
        
        for (int i = 1; i < len; ++i) {
            if (nums[i] > 0) {
                dpPosMax[i] = dpPosMax[i - 1] > 0 ? dpPosMax[i - 1] * nums[i] : nums[i];
                dpNegMin[i] = dpNegMin[i -1] < 0 ? dpNegMin[i - 1] * nums[i] : 0;
            } else if (nums[i] < 0) {
                dpPosMax[i] = dpNegMin[i - 1] < 0 ? dpNegMin[i - 1] * nums[i] : 0;
                dpNegMin[i] = dpPosMax[i - 1] > 0 ? dpPosMax[i - 1] * nums[i] : nums[i];
            }
            result = max(result, dpPosMax[i]);
        }
        return result;
    }
};

解法2:由解法1看出dpPosMax[i]和dpNegMin[i]都只取决于前面一个元素,所以采用滚动数组各用2个变量就可以了。
代码如下:
注意:因为posMaxNew/negMinNew没有缺省值,所以当nums[i]==0时,需要将它们设置为0。

class Solution {
public:
    /**
     * @param nums: An array of integers
     * @return: An integer
     */
    int maxProduct(vector<int> &nums) {
        int len = nums.size();
        if (len == 0) return 0;
        
        int posMaxNew = 0, posMaxOld = 0, negMinNew = 0, negMinOld = 0;
        int result = nums[0];
        
        if (nums[0] > 0) {
            posMaxNew = nums[0];
        }

        if (nums[0] < 0) {
            negMinNew = nums[0];
        }
        
        for (int i = 1; i < len; ++i) {
            if (nums[i] > 0) {
                int posMax = posMaxNew > 0 ? posMaxNew * nums[i] : nums[i];
                int negMin = negMinNew < 0 ? negMinNew * nums[i] : 0;
                posMaxOld = posMaxNew;
                posMaxNew = posMax;
                negMinOld = negMinNew;
                negMinNew = negMin;
            } else if (nums[i] < 0) {
                int posMax = negMinNew < 0 ? negMinNew * nums[i] : 0;
                int negMin = posMaxNew > 0 ? posMaxNew * nums[i] : nums[i];
                posMaxOld = posMaxNew;
                posMaxNew = posMax;
                negMinOld = negMinNew;
                negMinNew = negMin;
            } else {
                posMaxNew = 0;
                negMinNew = 0;
            }
            result = max(result, posMaxNew);
        }
        return result;
    }
};

二刷:

class Solution {
public:
    int maxProduct(vector<int>& nums) {
       int n = nums.size();
       if (n == 0) return 0;
       
        int maxProd = nums[0] > 0 ? nums[0] : 0;
        int minProd = nums[0] < 0 ? nums[0] : 0;
        int res = nums[0];
       
        for (int i = 1; i < n; i++) {
            if (nums[i] > 0) {
               maxProd = max(maxProd * nums[i], nums[i]);
               minProd = minProd < 0 ? minProd * nums[i] : 0;
            } else if (nums[i] < 0) {
               int origMaxProd = maxProd;
               maxProd = minProd < 0 ? minProd * nums[i] : 0;
               minProd = min(origMaxProd * nums[i], nums[i]);
            } else {
               maxProd = 0;
               minProd = 0;
            }
            res = max(res, maxProd);
        }
        return res;
    }
};

解法3:
参考自网上。因为每次的最大值都是在posMaxNew * nums[i], negMinNew * nums[i],nums[i]中间选,所以可以直接写成

            max = max(max(max * nums[i], min * nums[i]),nums[i]);
            min = min(min(min * nums[i],temp * nums[i]),nums[i]);

注意:
1) 当nums[i]==0时,max和min都自动清0。
2)一定要事先保存max的值(int temp = max),不然计算min的时候的max已经被更新了。

public class Solution {
    /**
     * @param nums: An array of integers
     * @return: An integer
     */
    // Time O(n)
    // Space O(1)
    public int maxProduct(int[] nums) {
        if(nums == null || nums.length == 0){
            return 0;
        }
        int min = nums[0];
        int max = nums[0];
        int result = nums[0];
        for(int i = 1; i < nums.length; i++){
            int temp = max;
            max = Math.max(Math.max(max * nums[i], min * nums[i]),nums[i]);
            min = Math.min(Math.min(min * nums[i],temp * nums[i]),nums[i]);
            result = Math.max(result,max);
        }
        return result;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值