1. 问题描述:
给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。你可以认为每种硬币的数量是无限的。
示例 1:
输入:coins = [1, 2, 5], amount = 11
输出:3
解释:11 = 5 + 5 + 1
示例 2:
输入:coins = [2], amount = 3
输出:-1
示例 3:
输入:coins = [1], amount = 0
输出:0
示例 4:
输入:coins = [1], amount = 1
输出:1
示例 5:
输入:coins = [1], amount = 2
输出:2
提示:
1 <= coins.length <= 12
1 <= coins[i] <= 2 ^ 31 - 1
0 <= amount <= 10 ^ 4
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/coin-change
2. 思路分析:
分析题目可以知道这道题目属于完全背包的问题,对于题目需要在一定条件下"凑"出对应的数值并且求解最优值的时候可以考虑一下是否是为背包问题模型。对于这道题目来说属于完全背包的裸题,我们可以将硬币的面额看成是物品的体积,因为求解的是使用硬币数目的最小数目所以物品的价值为1,总金额可以看成是背包的容量,这样就可以转换为完全背包的模型解决。dp数组或者列表的长度为当前背包的容量,所以为总金额加1,一开始的时候初始化dp元素值为最大值(求解的是最小值所以需要将数组或列表元素最大化),因为coins[i] <= 2 ^ 31 - 1所以coins[i]小于10 ^ 8所以最大值可以看成是10 ^ 8,并且dp[0] = 0表示在背包中没有物品的时候硬币数量为0。初始化之后就是完全背包的基本套路了,使用两层模拟即可第一层循环表示当前的物品,第二层循环表示当前物品的体积到背包容量的范围(模拟背包体积),这样我们在递推的时候就可以在背包容量允许的前提下拿取无限个当前的物品。
3. 代码如下:
from typing import List
class Solution:
def coinChange(self, coins: List[int], amount: int) -> int:
inf = 100000000
dp = [inf] * (amount + 1)
dp[0] = 0
for i in range(len(coins)):
# 从小到大枚举体积表示在当前背包容量允许的前提下拿取当前的物品是无限个的
for j in range(coins[i], amount + 1):
v = coins[i]
# 需要的硬币数加1
dp[j] = min(dp[j], dp[j - v] + 1)
return dp[amount] if dp[amount] != inf else -1