返回

掌握LeetCode 152:乘积最大子数组,解锁动态编程制胜之道

见解分享

动态编程的魅力:揭开 LeetCode 152 乘积最大子数组的奥秘

简介

在计算机科学领域,我们经常遇到看似棘手的问题,仅凭直觉很难解决。在这种情况下,一种称为动态编程的技术就会成为我们的救命稻草。本文将通过剖析 LeetCode 上经典的 152 题:乘积最大子数组,带你领略动态编程的魅力。

动态编程:分步求解的艺术

动态编程是一种解决复杂问题的策略,它将问题分解为一系列更小的子问题,并逐步求解。其核心思想是存储子问题的解,避免重复计算。这有点像爬楼梯,我们逐级攀登,每一步都依赖于之前走过的台阶。

LeetCode 152:乘积最大子数组

在 LeetCode 152 中,我们的目标是找出给定数组中乘积最大的连续子数组。例如,对于数组 [-2, 0, -1],乘积最大的子数组是 [-2, -1],其乘积为 2。

子问题的定义

为了应用动态编程,我们首先需要定义子问题。我们定义 dp[i][j] 为以索引 i 结尾、长度为 j+1 的子数组的乘积最大值。

子问题的求解

接下来,我们需要确定如何求解子问题。当 j=0 时,dp[i][0] = nums[i],即子数组只有一个元素。当 j>0 时,我们有两种情况:

  • 如果 nums[i] 为正数,那么 dp[i][j] = dp[i-1][j-1] * nums[i],因为正数相乘会使乘积更大。
  • 如果 nums[i] 为负数,那么 dp[i][j] = dp[i-1][j-1] / nums[i],因为负数相乘会使乘积更小,但除以一个负数会使结果变为正数。

初始化

我们从 dp[0][0] = nums[0] 开始初始化。

最终解

最终,最大乘积子数组的乘积为 max(dp[0][n-1], dp[1][n-1], ..., dp[n-1][n-1]),其中 n 是数组的长度。

代码实现

public int maxProduct(int[] nums) {
    int n = nums.length;
    int[][] dp = new int[n][n];
    for (int i = 0; i < n; i++) {
        dp[i][0] = nums[i];
    }
    int max = nums[0];
    for (int j = 1; j < n; j++) {
        for (int i = 0; i < n-j; i++) {
            int p = dp[i][j-1];
            if (nums[i+j] > 0) {
                dp[i][j] = p * nums[i+j];
            } else if (nums[i+j] < 0) {
                dp[i][j] = p / nums[i+j];
            } else {
                dp[i][j] = 0;
            }
            max = Math.max(max, dp[i][j]);
        }
    }
    return max;
}

总结

动态编程为解决复杂问题提供了有效的途径。通过将问题分解成更小的子问题,并逐步求解,我们可以轻松地找到最佳解。LeetCode 152:乘积最大子数组就是动态编程的一个经典应用,它完美诠释了这种方法的强大之处。

常见问题解答

  1. 什么是动态编程?
    动态编程是一种解决复杂问题的分步求解策略,通过存储子问题的解来避免重复计算。

  2. LeetCode 152 中如何定义子问题?
    dp[i][j] 定义为以索引 i 结尾、长度为 j+1 的子数组的乘积最大值。

  3. 如何求解子问题?
    根据 nums[i] 的正负性,子问题可以转换为 dp[i][j] = dp[i-1][j-1] * nums[i]dp[i][j] = dp[i-1][j-1] / nums[i]

  4. LeetCode 152 中的最终解如何求得?
    最终解为所有子问题解的最大值,即 max(dp[0][n-1], dp[1][n-1], ..., dp[n-1][n-1])

  5. 动态编程在其他问题中有哪些应用?
    动态编程广泛应用于计算机科学,包括最长公共子序列、背包问题、网格图搜索等。