返回

凑足总金额所需的硬币个数: 动态规划与动画演示

前端

零钱兑换问题的动态规划解决方案

在我们的日常生活中,我们经常需要凑成某个总金额,这被称为零钱兑换问题。例如,我们需要用不同面额的硬币凑成 13 元,可以使用 1 元硬币 3 枚、5 元硬币 1 枚来凑成。

本篇文章将探讨使用动态规划解决零钱兑换问题的方案,并通过动画演示和 Python 代码实现来帮助你理解其原理。

问题概述

零钱兑换问题如下:

给定一个整数数组 coins,表示不同面额的硬币;以及一个整数 amount,表示总金额。

求解凑成总金额所需的最少硬币数。

如果没有任何一种硬币组合能组成总金额,则返回 -1。

动态规划解决方案

动态规划是一种解决优化问题的常用方法,它将问题分解成一系列子问题,然后逐步解决这些子问题,最终得到整个问题的最优解。

对于零钱兑换问题,我们可以定义一个表格 dp,其中 dp[i] 表示凑成总金额 i 所需的最小硬币数。

我们首先将 dp[0] 设置为 0,因为凑成总金额 0 元不需要任何硬币。

然后,对于总金额 i > 0,我们可以考虑以下两种情况:

  • 如果 coins 中没有面额为 i 的硬币,那么凑成总金额 i 所需的最小硬币数为 -1,因为没有任何一种硬币组合能组成总金额 i
  • 如果 coins 中有面额为 i 的硬币,那么凑成总金额 i 所需的最小硬币数可以表示为:
dp[i] = min(dp[i - 1], dp[i - 2], ..., dp[i - coins[j]]) + 1

其中,jcoins 中面额为 i 的硬币的索引。

这是因为,如果我们想要凑成总金额 i,我们可以使用以下两种方式:

  • 使用面额为 i 的硬币凑成总金额 i
  • 使用面额小于 i 的硬币凑成总金额 i - 1i - 2、...、i - coins[j],然后加上一个面额为 i 的硬币。

我们选择这两种方式中所需的最少硬币数,加上 1,就是凑成总金额 i 所需的最小硬币数。

动画演示

为了帮助你更好地理解动态规划的解决方案,我们提供了一个动画演示:

动画演示链接

Python 代码实现

以下是如何使用 Python 实现动态规划解决方案的代码:

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

  Args:
    coins (list): 不同面额的硬币
    amount (int): 总金额

  Returns:
    int: 最小硬币数
  """

  # 初始化表格
  dp = [float('inf')] * (amount + 1)
  dp[0] = 0

  # 遍历硬币面额
  for coin in coins:
    # 遍历总金额
    for i in range(coin, amount + 1):
      # 如果总金额大于或等于硬币面额
      if i >= coin:
        # 更新表格
        dp[i] = min(dp[i], dp[i - coin] + 1)

  # 返回凑成总金额所需的最小硬币数
  return dp[amount] if dp[amount] != float('inf') else -1

常见问题解答

1. 什么是动态规划?

动态规划是一种解决优化问题的常用方法,它将问题分解成一系列子问题,然后逐步解决这些子问题,最终得到整个问题的最优解。

2. 为什么使用动态规划解决零钱兑换问题?

动态规划是一种解决零钱兑换问题的有效方法,因为它可以将问题分解成一系列子问题,并逐步解决这些子问题,从而得到最优解。

3. 动画演示如何帮助理解动态规划解决方案?

动画演示通过可视化的方式展示了动态规划解决方案的每一步,帮助读者理解其原理和过程。

4. Python 代码实现中 dp[i] 的意义是什么?

dp[i] 表示凑成总金额 i 所需的最小硬币数。

5. 如何判断是否可以凑成总金额?

如果 dp[amount] 不等于 float('inf'),则表示可以凑成总金额;否则,无法凑成总金额。