package dynamic_programming; /** * 题目: 152. 乘积最大子数组 (MaxProduct) * 描述:给你一个整数数组 nums ,请你找出数组中乘积最大的非空连续 子数组(该子数组中至少包含一个数字),并返回该子数组所对应的乘积。 * 测试用例的答案是一个 32-位 整数。 * 示例 1: 输入: nums = [2,3,-2,4] 输出: 6 解释: 子数组 [2,3] 有最大乘积 6。 * 链接:https://leetcode.cn/problems/maximum-product-subarray/ */ //不会 //二刷不会 public class MaxProduct { /** * 用动态规划维护遍历到当前位置时,以 nums[i] 结尾的「最大乘积」和「最小乘积」: * - prevMax:前一步的最大乘积 * - prevMin:前一步的最小乘积(因为负数相乘会翻转大小) * 对于新元素 x = nums[i],候选值只有三种: * 1. 单独以 x 为一段子数组 * 2. 将 x 与 prevMax 相乘(延续最大乘积序列) * 3. 将 x 与 prevMin 相乘(延续最小乘积序列,有可能翻转成最大) * 因此通过 max(x, x*prevMax, x*prevMin) 得到新的 currMax, * 同理用 min(x, x*prevMax, x*prevMin) 得到 currMin。 * 然后更新全局答案 ans = max(ans, currMax)。 */ public int maxProduct(int[] nums) { if (nums == null || nums.length == 0) { throw new IllegalArgumentException("数组不能为空"); } // 初始化:第 0 个位置的最大/最小乘积都等于 nums[0] int prevMax = nums[0]; int prevMin = nums[0]; int ans = nums[0]; // 从 i=1 开始遍历 for (int i = 1; i < nums.length; i++) { int x = nums[i]; // 三个候选值:x、x*prevMax、x*prevMin int candidateMax = Math.max(x, Math.max(x * prevMax, x * prevMin)); int candidateMin = Math.min(x, Math.min(x * prevMax, x * prevMin)); // 更新当前的最大/最小乘积 prevMax = candidateMax; prevMin = candidateMin; // 更新全局答案 ans = Math.max(ans, prevMax); } return ans; } // 简单测试 public static void main(String[] args) { MaxProduct sol = new MaxProduct(); System.out.println(sol.maxProduct(new int[]{2, 3, -2, 4})); // 输出 6 System.out.println(sol.maxProduct(new int[]{-2, 0, -1})); // 输出 0 System.out.println(sol.maxProduct(new int[]{-2, 3, -4})); // 输出 24 System.out.println(sol.maxProduct(new int[]{-1, -3, -10, 0, 60})); // 输出 60 System.out.println(sol.maxProduct(new int[]{-2, -5, -2, -4, 3})); // 输出 240 } }