问题:
给定不同面额的硬币(coins)和一个总金额(amount)。写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合方式能组成总金额,返回-1
。
示例 1:
coins = [1, 2, 5]
, amount = 11
return 3
(11 = 5 + 5 + 1)
示例 2:
coins = [2]
, amount = 3
return -1
.
注意:
你可以认为每种硬币的数量是无限的。(此题目摘自leetcode)
什么是动态规划?其思路是为了求解当前的问题的最优解,使用子问题的最优解,然后综合处理,最终得到原问题的最优解。网上有很多介绍的文章,但是对于初学者(比如我)来说,看起来会有些懵。我个人的理解是,当总金额为amount时,所需的最少硬币个数为dp[amount],那么当amount = 11时,求出所有dp[1]、dp[2]、…、dp[11]的值。dp[1]到dp[10]就可以说是dp[11]的子问题,需要通过他们来求最终解。
以示例1为例,我们需要尽可能少的硬币个数,所以从11的总金额中取出任意一枚硬币,剩下的金额所需最少硬币个数再加上1就是所需硬币个数,即所需硬币个数为:dp[10]+1、dp[9]+1、dp[6]+1,再从中取最小值,即可求解。以此类推,dp[10]=min(dp[9]+1,dp[8]+1,dp[5]+1)…所以可以看出,通过求出所有dp[1]、dp[2]、…、dp[10]的值,最终就能得到dp[11]的值。
以下是代码实现:
def coinChange(coins, amount):
“””
:type coins: List[int]
:type amount: int
:rtype: int
“””
coins.sort() #给硬币从小到大排序
dp = {0:0} #生成字典dp,并且当总金额为0时,最少硬币个数为0
for i in range(1,amount + 1):
dp[i] = amount + 1 #因为硬币个数不可能大于amount,所以赋值amount + 1便于比较
for j in coins:
if j <= i:
dp[i]=min(dp[i],dp[i-j]+1)
#for i in range(1,amount + 1):
#print(‘dp[%d]:’%(i), dp[i])
if dp[amount] == amount + 1: #当最小硬币个数为初始值时,代表不存在硬币组合能构成此金额
return -1
else:
return dp[amount]
一开始不太理解,为什么要求金额11的解需要算出1-10的所有解,这样不是把问题弄得更复杂了吗?后来醒悟是思路问题,这有些递归的思想在里边,就像盖楼一样,你不可能不用1-10层就只盖个11层。之所以写这篇文章,是因为想保留我现在作为初学者的视角,等以后学习更深入些再回过头来说不定会有新的感悟,望大佬轻喷