返回

动态规划系列之力扣中等题(零钱兑换,乘积最大子数组,最长递增子序列,带详细注释)

前端

前言

在前一章中,我们介绍了动态规划的基本概念并用一道简单的题目来学习动态规划。在这一章中,我们将介绍三道力扣中等难度的题目来巩固我们的学习。这三道题分别是:

  • 零钱兑换
  • 乘积最大子数组
  • 最长递增子序列

零钱兑换

给定一组面额和一个目标金额,求出用这些面额的硬币凑成目标金额的最小硬币数。

def coinChange(coins, amount):
    """
    :type coins: List[int]
    :type amount: int
    :rtype: int
    """
    # dp[i]表示凑成金额i所需要的最少硬币数
    dp = [amount + 1] * (amount + 1)

    # 初始状态:dp[0] = 0
    dp[0] = 0

    # 对于每个硬币面额,计算凑成每个金额所需的最小硬币数
    for coin in coins:
        for i in range(coin, amount + 1):
            dp[i] = min(dp[i], dp[i - coin] + 1)

    # 如果dp[amount]为amount + 1,则表示无法凑成目标金额
    if dp[amount] == amount + 1:
        return -1
    else:
        return dp[amount]

乘积最大子数组

给定一个整数数组,求出该数组中乘积最大的连续子数组的乘积。

def maxProduct(nums):
    """
    :type nums: List[int]
    :rtype: int
    """
    # dp[i][0]表示以nums[i]结尾的连续子数组的最大乘积
    # dp[i][1]表示以nums[i]结尾的连续子数组的最小乘积
    dp = [[1, 1] for _ in range(len(nums))]

    # 初始状态:dp[0][0] = nums[0], dp[0][1] = nums[0]
    dp[0][0] = nums[0]
    dp[0][1] = nums[0]

    # 对于每个元素,计算以该元素结尾的连续子数组的最大乘积和最小乘积
    for i in range(1, len(nums)):
        if nums[i] >= 0:
            dp[i][0] = max(nums[i], dp[i - 1][0] * nums[i])
            dp[i][1] = min(nums[i], dp[i - 1][1] * nums[i])
        else:
            dp[i][0] = max(nums[i], dp[i - 1][1] * nums[i])
            dp[i][1] = min(nums[i], dp[i - 1][0] * nums[i])

    # 返回dp中最大的乘积
    return max(dp, key=lambda x: x[0])[0]

最长递增子序列

给定一个整数数组,求出该数组中最长的递增子序列的长度。

def longestIncreasingSubsequence(nums):
    """
    :type nums: List[int]
    :rtype: int
    """
    # dp[i]表示以nums[i]结尾的最长递增子序列的长度
    dp = [1] * len(nums)

    # 对于每个元素,计算以该元素结尾的最长递增子序列的长度
    for i in range(1, len(nums)):
        for j in range(i):
            if nums[i] > nums[j]:
                dp[i] = max(dp[i], dp[j] + 1)

    # 返回dp中最大的长度
    return max(dp)

总结

这三道题目都是经典的动态规划题目,它们都非常适合初学者学习动态规划。如果您对动态规划感兴趣,那么强烈建议您尝试一下这三道题目。