零钱兑换问题

零钱兑换问题是一个经典的动态规划问题,也是算法和编程中常见的一个问题。这个问题的描述是:给定不同面额的硬币 coins 和一个总金额 amount,求出能够凑成总金额所需的最少硬币数量。假设每种硬币的数量是无限的。

动态规划解法

动态规划是一种解决复杂问题的优秀算法思想。它通过将问题分解为更小的子问题,并通过保存子问题的解来构建原始问题的解。对于零钱兑换问题,我们可以使用动态规划来求解。

首先,我们定义一个长度为 amount+1 的数组 dp,其中 dp[i] 表示凑成金额 i 所需的最少硬币数量。我们将 dp 数组初始化为一个足够大的值(比如 amount+1),表示无法凑成该金额。

接下来,我们遍历从 0 到 amount 的每个金额,对于每个金额需要遍历所有的硬币面额。对于每个金额 i,我们可以选择使用某个硬币面额 j,此时凑成金额 i 的最少硬币数量为 dp[i-j]+1。我们更新 dp[i] 为所有可能的硬币面额 j 中的最小值。

最后,我们返回 dp[amount],即为凑成总金额所需的最少硬币数量。

下面是使用 Python 实现的动态规划解法的代码示例:

def coinChange(coins, amount):
    dp = [amount + 1] * (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] <= amount else -1

示例

假设有以下硬币面额和总金额:

硬币面额 总金额
1 11
2
5

我们可以使用动态规划解法来计算凑成总金额 11 所需的最少硬币数量。

首先,我们初始化 dp 数组为 [12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12],表示金额 0 到 11 都无法凑成。

接下来,我们从金额 1 开始遍历,对于每个金额,遍历所有的硬币面额。对于金额 1,我们遍历硬币面额 1,2,5,找到最小的硬币数量 1,并更新 dp[1] 为 1。

然后,我们继续遍历金额 2,对于金额 2,我们遍历硬币面额 1,2,5,找到最小的硬币数量 1,并更新 dp[2] 为 1。

同理,我们继续遍历金额 3,4,直到 11,最终得到 dp 数组为 [0, 1, 1, 2, 2, 1, 2, 2, 3, 3, 2, 3]。

所以,凑成总金额 11 所需的最少硬币数量为 3。

可以使用以下代码验证上述示例:

print(coinChange([1, 2, 5], 11))  # 输出 3

总结

零钱兑换问题是一个经典的动态规划问题,使用动态规划可以有效地求解。通过定义一个 dp 数组,保存每个金额所需的最少硬币数量,我们可以逐步构建解决方案。使用动态规划的思想,可以有效地解决各种复杂的问题,是算法和编程中重要的思维工具。

希望本文对你理解零钱兑换问题和动态规划有所帮助!