返回

乘风破浪,携手探索LeetCode动态规划之乘积最大子数组

后端

动态规划:破解乘积最大子数组难题

导言

计算机科学世界中,动态规划是一种强大的算法设计方法,它在解决各种复杂问题时大显身手,包括最优决策问题和优化问题。通过将问题分解为更小的子问题并保存它们的解决方案,动态规划可以显著减少计算量。其中,乘积最大子数组问题就是一个经典的动态规划难题。

乘积最大子数组问题

问题

给定一个整数数组 nums ,找出数组中乘积最大的连续子数组(至少包含一个数字)。

示例:

  • 示例 1:
    • 输入:nums = [2, 3, -2, 4]
    • 输出:6
    • 解释:子数组 [2, 3] 具有最大乘积 6。
  • 示例 2:
    • 输入:nums = [-2, 0, -1]
    • 输出:0
    • 解释:结果不能为负数。
  • 示例 3:
    • 输入:nums = [-2, 3, -4]
    • 输出:24
    • 解释:子数组 [-2, -3, -4] 具有最大乘积 24。

挑战:

能否设计一种时间复杂度为 O(n) 和空间复杂度为 O(1) 的算法来解决此问题?

解题思路

动态规划解决乘积最大子数组问题的核心在于将复杂问题分解为更小的子问题。我们定义一个状态 dp[i] ,它表示以 nums[i] 结尾的子数组的最大乘积。

状态转移方程:

  • 如果 nums[i] > 0 ,则 dp[i] = dp[i-1] * nums[i]
  • 如果 nums[i] < 0 ,则 dp[i] = dp[i-1] * nums[i]dp[i] = nums[i] ,取较大值。
  • 如果 nums[i] = 0 ,则 dp[i] = 0

初始化:

dp[0] = nums[0]

计算:

i = 1 开始,逐个计算 dp[i]

答案:

最大子数组乘积为 max(dp)

代码实现:

def max_product(nums):
  """
  Finds the maximum product of a contiguous subarray in a given array.

  Args:
    nums: A list of integers.

  Returns:
    The maximum product of a contiguous subarray in nums.
  """

  # Initialize the dp array.
  dp = [0] * len(nums)
  dp[0] = nums[0]

  # Calculate the maximum product of each subarray.
  for i in range(1, len(nums)):
    if nums[i] > 0:
      dp[i] = max(dp[i-1] * nums[i], nums[i])
    else:
      dp[i] = max(dp[i-1] * nums[i], nums[i])

  # Return the maximum product.
  return max(dp)

总结

乘积最大子数组问题的关键在于将复杂问题分解成更小的子问题。通过定义状态,建立状态转移方程,并进行初始化和计算,我们可以高效地找到数组中乘积最大的子数组。掌握动态规划的技巧对于解决此类问题至关重要。

常见问题解答

  1. 什么是动态规划?
    动态规划是一种通过将复杂问题分解为较小子问题并保存子问题解决方案来解决问题的算法设计方法。
  2. 如何使用动态规划解决乘积最大子数组问题?
    我们将问题分解为子问题,定义以每个元素结尾的子数组的最大乘积状态,并建立状态转移方程,以计算每个子问题的解。
  3. 为什么使用动态规划可以减少计算量?
    动态规划避免了重复计算,因为它保存了子问题的解决方案,从而显著减少了计算量。
  4. 空间复杂度为 O(1) 意味着什么?
    这意味着算法仅使用恒定数量的额外空间,无论输入大小如何。
  5. 为什么乘积最大子数组问题需要考虑负数?
    因为负数可以导致子数组乘积取最大值,就像示例 3 中的子数组 [-2, -3, -4] 一样。