【Hot 100 刷题计划】 LeetCode 152. 乘积最大子数组 | C++ 动态规划 (绝妙 swap 翻转技巧)

LeetCode 152. 乘积最大子数组

📌 题目描述

题目级别:中等

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

测试用例的答案是一个 32-位 整数。

  • 示例 1:

    输入: nums = [2,3,-2,4]

    输出: 6

    解释: 子数组 [2,3] 有最大乘积 6

  • 示例 2:

    输入: nums = [-2,0,-1]

    输出: 0

    解释: 结果不能为 2, 因为 [-2,-1] 不是子数组(不连续)。


💡 破题思路:动态规划的"负负得正"陷阱

这道题和"最大子数组和"非常像,但乘法有一个致命的特殊性:负号的存在

一个极其小的负数,如果再乘上一个负数,会瞬间翻盘变成一个巨大的正数!

因此,我们不能只记录"当前的最大乘积",我们还必须同时记录"当前的最小乘积"(因为它有潜力在遇到下一个负数时变身成最大乘积)。

本解法的极客高光点 (Swap 魔法):

常规思路是在每次遍历时,同时用 nums[i]nums[i] * max_dpnums[i] * min_dp 三个数去求新的最大值和最小值,代码写起来非常繁琐。

但我们回归数学本质:当遇到一个负数时,乘积的大小关系会发生反转

所以,如果我们发现当前数字 nums[i] < 0,我们直接把维护的"当前最大值 mam"和"当前最小值 mim"互换位置 (swap)

互换之后,我们就可以像处理正数一样,毫无顾忌地直接更新 mammim 了。这个思路极其优雅,堪称神来之笔。


💻 C++ 代码实现 (O(1) 空间极简版)

cpp 复制代码
class Solution {
public:
    int maxProduct(vector<int>& nums) {
        // mim 维护以当前元素结尾的最小乘积
        // mam 维护以当前元素结尾的最大乘积
        int mim = 1, mam = 1; 
        int res = INT_MIN; // 全局最大结果

        for (int i = 0; i < nums.size(); i ++ )
        {
            // 绝妙逻辑:遇到负数,乘积的极值属性反转,直接 swap!
            if (nums[i] < 0)
            {
                swap(mim, mam);
            }

            // 要么是从前面连乘过来的,要么就是从当前元素重新开始自己单干
            mim = min(nums[i], mim * nums[i]);
            mam = max(nums[i], mam * nums[i]);

            // 每次更新全局最大值
            res = max(res, mam);
        }

        return res;
    }
};
相关推荐
十五年专注C++开发10 分钟前
fatal error C1189: #error: WinSock.h has already been included 的解决方案
c++·socket·boost
旖-旎3 小时前
《LeetCode 53 最大子数组和 || LeetCode 918 环形子数组的最大和》
c++·算法·leetcode·动态规划
c238564 小时前
《动态规划:从“傻傻穷举”到“过目不忘”的修仙之路》
c++·算法·动态规划
王老师青少年编程5 小时前
csp信奥赛C++高频考点专项训练:【二分答案】案例2:木材加工
c++·二分答案·csp·高频考点·信奥赛·木材加工
海石5 小时前
单调栈复健,顺便,牺牲一下吧,空间复杂度!一切献给AC
算法·leetcode
海石5 小时前
JS击败94%,Hard题想不到动态规划,那就用数组和栈试试
算法·leetcode
aaPIXa6226 小时前
C++模板元编程:编译期计算Fibonacci数列
java·开发语言·c++
Augustzero6 小时前
`co_await` 按下暂停键之后:从零看懂 C++20 协程
c++·后端
知无不研6 小时前
c语言和c++中的静态关键字
开发语言·c++·静态关键字
思麟呀7 小时前
在C++基础上理解CSharp-7
java·jvm·c++·c#