零钱兑换问题
零钱兑换问题是一个经典的动态规划问题,也是算法和编程中常见的一个问题。这个问题的描述是:给定不同面额的硬币 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 数组,保存每个金额所需的最少硬币数量,我们可以逐步构建解决方案。使用动态规划的思想,可以有效地解决各种复杂的问题,是算法和编程中重要的思维工具。
希望本文对你理解零钱兑换问题和动态规划有所帮助!