返回

动态规划——零钱问题自我理解

前端

引言

在算法世界里,动态规划(Dynamic Programming)是一个重要的算法设计思想。它通过将一个大问题分解成一系列较小的子问题,然后依次解决这些子问题,最终解决大问题。动态规划算法在解决许多实际问题中都有着广泛的应用,如零钱问题、背包问题、最长公共子序列问题、编辑距离问题等等。

零钱问题

零钱问题是一个经典的动态规划问题。问题如下:

给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。

例如,给定硬币面额 coins = [1, 2, 5] 和总金额 amount = 11,则可以凑成总金额 11 的硬币组合有:

  • 1 个 5 元硬币、1 个 2 元硬币和 4 个 1 元硬币,共 6 个硬币。
  • 2 个 5 元硬币和 1 个 1 元硬币,共 3 个硬币。

因此,最少的硬币个数为 3。

动态规划解法

零钱问题的动态规划解法步骤如下:

  1. 定义状态:设 dp[i] 表示凑成总金额 i 所需的最少的硬币个数。
  2. 状态转移方程:对于总金额 i,如果硬币面额 coins[j] 小于或等于 i,那么凑成总金额 i 的硬币个数 dp[i] 可以由以下两种情况得到:
    • 如果不使用硬币面额 coins[j],那么凑成总金额 i 的硬币个数为 dp[i] = dp[i]。
    • 如果使用硬币面额 coins[j],那么凑成总金额 i 的硬币个数为 dp[i] = min(dp[i], dp[i - coins[j]] + 1)。
  3. 初始化:dp[0] = 0,表示凑成总金额 0 所需的最少的硬币个数为 0。
  4. 计算:从 1 开始,依次计算 dp[1]、dp[2]、...、dp[amount]。
  5. 结果:返回 dp[amount]。

代码实现

以下是用 Python 实现的零钱问题的动态规划算法:

def coin_change(coins, amount):
    """
    计算凑成总金额所需的最少的硬币个数。

    Args:
        coins: 不同面额的硬币。
        amount: 总金额。

    Returns:
        凑成总金额所需的最少的硬币个数。
    """

    # 定义状态
    dp = [float('inf')] * (amount + 1)
    dp[0] = 0

    # 状态转移
    for i in range(1, amount + 1):
        for coin in coins:
            if coin <= i:
                dp[i] = min(dp[i], dp[i - coin] + 1)

    # 结果
    return dp[amount] if dp[amount] != float('inf') else -1

结语

动态规划算法是一种强大的算法设计思想,它可以帮助我们解决许多实际问题。零钱问题是一个经典的动态规划问题,通过将问题分解成一系列子问题,我们可以逐步解决问题,最终得到最优解。希望本文对您理解动态规划算法有所帮助。