518. 零钱兑换Ⅱ
题目链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台
思路
本题相当于求装满容量为amount的背包有多少种方法,又由于零钱可以重复使用,属于完全背包的题型。将01背包完全装满的思路在494. 目标和中介绍过,在完全背包的理论基础中,知道它与01背包的接替区别只在于遍历顺序,因此此题很容易便能写出。代码随想录算法训练营第四十八天(动态规划篇之01背包)| 1049. 最后一块石头的重量Ⅱ,494. 目标和-CSDN博客代码随想录算法训练营第四十九天(动态规划篇)| 474. 一和零, 完全背包理论基础-CSDN博客
1. dp数组定义
dp[j]:装满容量为j的完全背包有dp[j]种方法。
2. 递推公式
对于面值为i的零钱,它和之前遍历过的零钱凑成总金额amount的方法取决于之前的硬币能凑成总金额(amount-i)的方法,把这个零钱i能凑成的方法加入到dp[j]中。因此,递推公式为:
dp[j] += dp[amount - i]
3. 初始条件
dp[0]要为1,如果是0,那么之后递推出来的dp值都为0。dp[0] = 1可以认为凑成零就是不拿出任何零钱这一种方法。dp[0]=1还说明了一种情况:如果正好选了面值为j的零钱,也就是j-coins[i] == 0的情况,表示这个硬币刚好能选,此时dp[0]为1表示只选coins[i]存在这样的一种选法。其他下标的dp值保持为0就可。
4. 遍历顺序
对完全背包求总和的最大价值,容量从小到大遍历,可以外层遍历物体,内层遍历容量,也可以外层遍历容量,内层便遍历物体,和凑成总和的元素有没有顺序没关系。但对于这道题,我们要求凑成总和的元素是无序的,即{1,4}和{4,1}都能凑成5,但它们是一种方法,而非两种。分别考虑两种遍历顺序
1. 外层遍历容量,内层遍历物体
如果先遍历容量,再遍历物体,对当前容量,假设遍历第一个物体,发现与第三个物体能凑成总和,方法数加了1,那继续遍历到第三个物体时,又发现能与第一个物体凑成,方法数又加了1,就重复了。
2. 外层遍历物体,内层遍历容量
如果先遍历物体,再遍历容量,就是先把第一个物体加入运算,那时第三个物体还没加入,所以没有凑成,等遍历到第三个物体时,才能和第一个物体凑成,不会造成重复。
因此,应该外层遍历物体,内层遍历容量。
5. 举例推导dp数组
输入: amount = 5, coins = [1, 2, 5] ,dp状态图如下:
代码实现
class Solution(object):def change(self, amount, coins):dp = [0]*(amount + 1)dp[0] = 1for coin in coins: for j in range(coin, amount + 1):dp[j] += dp[j-coin]return dp[amount]