参考zzu_Lee https://www.cnblogs.com/hengzhezou/p/11042906.html 感谢
题目描述:
给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。
示例 1:
只有面额为 1元、2元、5元的三类硬币,每种数量无限。如果需要11元钱,最少的硬币数量是多少呢?
输入: coins = [1, 2, 5], amount = 11
输出: 3
解释: 11 = 5 + 5 + 1
示例 2:
只有面额为 2元的一类硬币,数量无限。如果需要3元钱,最少的硬币数量是多少呢?
输入: coins = [2], amount = 3
输出: -1
说明:某些数值,就是无法取出的。没有组合的方案。
思路一 :贪心算法
贪心思想:直接先用最大面值的去减。
示例一
例如:面额为【1,2,5,10】,求14元
按照贪心思想:
- 14-10=4
- 4-2=2
- 2-2=0
则一共需要 10 2 2 三个硬币。
贪心思想成功了!!
示例二
例如:面额为【1,2,5,7,10】,求14元
如果利用贪心思想,应该是 10 2 2 三个硬币。
但是 7+7 2枚硬币,才是最优的答案。
因此贪心算法先天是有缺陷的
引入 动态规划 思想
动态规划的核心就是 寻找 状态转移方程
思路:
面额为【1,2,5】,求14元
首先 存储 0-13元之间的每一个最优解dp[i]
解释一下
dp[0]=0 需要0元的时候的结果
dp[1]=1 需要1元的时候,直接使用面额为1的1个硬币。结果为1
dp[2]=1 需要2元的时候,直接使用面额为2的1个硬币。结果为1
dp[3]=2 需要3元的时候,直接使用 面额为2的1个硬币 + 面额为1的1个硬币 。结果为2
dp[4]=2 需要4元的时候,直接使用 面额为2的2个硬币 。结果为2
dp[5]=1 需要5元的时候,直接使用 面额为5的1个硬币 。结果为1
dp[6]=2 需要6元的时候,直接使用 面额为1的1个硬币 + 面额为5的1个硬币 。结果为2
dp[7]=2 需要7元的时候,直接使用 面额为2的1个硬币 + 面额为5的1个硬币 。结果为2
dp[8]=3 需要8元的时候,直接使用 面额为1的1个硬币 + 面额为2的1个硬币 + 面额为5的1个硬币 。结果为3
dp[9]=3 需要9元的时候,直接使用 面额为2的2个硬币 + 面额为5的1个硬币 。结果为3
dp[10]=2 需要10元的时候,直接使用 面额为5的2个硬币 。结果为2
dp[11]=3 需要11元的时候,直接使用 面额为5的2个硬币 + 面额为1的1个硬币 。结果为3
dp[12]=3 需要12元的时候,直接使用 面额为5的2个硬币 + 面额为2的1个硬币 。结果为3
dp[13]=4 需要13元的时候,直接使用 面额为5的2个硬币 + 面额为2的1个硬币 + 面额为1的1个硬币。结果为4
dp[i]就是每一步的运算结果。动态规划的核心就是: 记录下每一步的结果,然后以递增的方式增加。
状态转移方程就是:
理解一下下一步 的思路
- di[14]=min(di[14-1],di[14-2],di[14-5])+1
- di[14]=min(di[13],di[12],di[9])+1
- di[14]=min(4,3,3)+1
- di[14]=4
文字解读一下:
相当于要求14元的时候,先看一下有哪些币种,【1,2,5】,
则 我们分别减去 后,为 dp[13],dp[12],dp[9],
(
- 相当于在dp[13]状态时,加上一个1元的币;
- 或 相当于在dp[12]状态时,加上一个2元的币;
- 或 相当于在dp[9]状态时,加上一个5元的币;)
再补上1个币。然后求他们的最小值。
这里加1 ,其实应该在里面,不过经过公式推算,放在了外面。
对于那些无法计算得出的数值,直接记为 正无穷(因为我们是比较MIN值)
class Solution(object):
def coinChange(self, coins, amount):
"""
:type coins: List[int]
:type amount: int
:rtype: int
"""
n = len(coins)
# dp[i]表示amount=i需要的最少coin数
dp = [float("inf")] * (amount+1) # float("inf") 表示正无穷。初始化数组
dp[0] = 0 #初始化第一个值记为0
for i in range(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