动态规划-乘积最大子数组

题目描述

给你一个整数数组 nums ,请你找出数组中乘积最大的非空连续 子数组 (该子数组中至少包含一个数字),并返回该子数组所对应的乘积。 测试用例的答案是一个 32-位 整数。

152. 乘积最大子数组 - 力扣(LeetCode)

解题思路

使用了两个动态规划数组 f 和 g 来分别跟踪到当前位置为止,乘积最大和最小的子数组乘积。这是基于以下观察:

  1. 最大乘积(f 数组):在遍历数组时,对于每个位置 i,最大乘积要么是当前元素 nums[i] 本身(如果前面的元素都是负数或者乘积小于当前元素),要么是前面位置的最大乘积 f[i-1] 乘以当前元素 nums[i](如果前面的乘积是正数或者0)。但是,当遇到负数时,我们需要考虑前一个位置的最小乘积 g[i-1] 乘以当前负数 nums[i] 是否能产生新的最大乘积(因为负数乘以负数会变成正数)。

  2. 最小乘积(g 数组):与最大乘积类似,最小乘积的更新也考虑了当前元素、前一个位置的最小乘积乘以当前元素,以及前一个位置的最大乘积乘以当前元素(当当前元素为负数时)。这是因为,负数乘以正数(前一个位置的最大乘积)会得到一个更小的负数,这可能是新的最小乘积。

上述的动态规划思路与此前的最大子数组和一题相似。

在遍历过程中,f 和 g 数组被逐步构建,并且我们始终保持 f 数组中的最大值作为当前已知的最大乘积。遍历完成后,f 数组中的最大值即为所求的最大乘积子数组的乘积。

代码示例

class Solution {
public:
    int maxProduct(vector<int>& nums) {
        int n = nums.size();
        vector<int> f(n, 0); // 乘积最大的子数组
        vector<int> g(n, 0); // 乘积最小的子数组

        f[0] = g[0] = nums[0];
        for (int i = 1; i < n; i++) {
            if (nums[i] > 0) {
                f[i] = max(nums[i] * f[i - 1], nums[i]);
                g[i] = min(nums[i], nums[i] * g[i - 1]);
            } else {
                f[i] = max(nums[i], nums[i] * g[i - 1]);
                g[i] = min(nums[i], nums[i] * f[i - 1]);
            }
        }
        int ret = f[0];
        for (int i = 1; i < n; i++) {
            ret = max(ret, f[i]);
        }
        return ret;
    }
};

 

以下是代码的主要步骤:

  • 初始化 f[0] 和 g[0] 为 nums[0],因为第一个元素既是最大乘积也是最小乘积的起点。
  • 遍历数组 nums,从第二个元素开始:
    • 如果当前元素 nums[i] 是正数,则最大乘积 f[i] 可以是 nums[i] 或者 nums[i] * f[i-1](取决于哪个更大),最小乘积 g[i] 可以是 nums[i] 或者 nums[i] * g[i-1](取决于哪个更小)。
    • 如果当前元素 nums[i] 是负数,则最大乘积 f[i] 可以是 nums[i] 或者 nums[i] * g[i-1](因为负数乘以负数变正数,可能成为新的最大乘积),最小乘积 g[i] 可以是 nums[i] 或者 nums[i] * f[i-1](因为负数乘以正数变得更小)。
  • 遍历完成后,f 数组中的最大值即为所求的最大乘积。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值