Leetcode-152 乘积最大子数组

题目描述

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

示例1:

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

示例2:

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

解题思路

一种错误的解题思路

dp[i]表示以第i个元素结尾的乘积最大子数组的乘积,你可能会想到这样的转移方程 dp[i] = max(dp[i-1] * nums[i], dp[i-1]),但是当前位置的最优解未必是由前一个位置的最优解转移得到!比如[3, 4, -2, 7, -1],我们使用上述转移方程,最后的结果是12,实际上应该是3 * 4 * (-2) * 7 * (-1)。

正确的思路(一)

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

C++代码

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

正确的思路(二)

imax 为当前最大值, imin 为当前最小值,考虑到 nums[i]正负的情况,那么最大值res可以这样求取:

  1. nums[i] > 0时:imax = max(imax * nums[i], nums[i])imin = min(imin * nums[i], nums[i])
  2. nums[i] <= 0时:imax = max(imin * nums[i], nums[i])imin = min(imax* nums[i], nums[i])
    在此过程更新最大值res就可以

C++代码

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

这里,可以进行逻辑的简化:如果 nums[i] < 0,我将imax 和 imin 互换即可,那么当前最大值和最小值就可以写成一种形式:

class Solution {
public:
    int maxProduct(vector<int>& nums) {
        int res = INT_MIN, imax = 1, imin = 1;
        for (int i = 0; i < nums.size(); i++) {
            if (nums[i] < 0) {
                int tmp = imax;
                imax = imin;
                imin = tmp;
            }
            imax = max((imax * nums[i]), nums[i]);
            imin = min((imin * nums[i]), nums[i]);

            res = max(res, imax);
        }
        return res;
    }
};

  • 19
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值