leetcode518.零钱兑换 II

1.题目描述

给定不同面额的硬币和一个总金额。写出函数来计算可以凑成总金额的硬币组合数。假设每一种面额的硬币有无限个。 

 

示例 1:

输入: amount = 5, coins = [1, 2, 5]
输出: 4
解释: 有四种方式可以凑成总金额:
5=5
5=2+2+1
5=2+1+1+1
5=1+1+1+1+1
示例 2:

输入: amount = 3, coins = [2]
输出: 0
解释: 只用面额2的硬币不能凑成总金额3。
示例 3:

输入: amount = 10, coins = [10] 
输出: 1
 

注意:

你可以假设:

0 <= amount (总金额) <= 5000
1 <= coin (硬币面额) <= 5000
硬币种类不超过 500 种
结果符合 32 位符号整数

2.解题思路

类比零钱兑换第一题,每个面值的钱可以使用任意多次,我们可以构造一个dp数组,如dp数组的行数为N,列数为aim+1,dp[i][j] 的含义是:在可以任意使用arr[0..i]货币的情况下,组成钱数j有多少张方法。

第一行dp[0][0..aim]中每一个元素dp[0][j]表示用arr[0]货币找开面额 j的方法,此时我们只能选取arr[0]这一张货币,所以只有arr[0]的整数倍的面额钱才可以找开,例如当arr[0]=3,aim=10时,只能找开3,6,9的货币,且只有一种方法即只是用arr[0],而其他面额的则无法找开,所以将arr[0][3,6,9]初始化为1 除此之外其他值初始化为0表示无法找开。对于第一列dp[0..n][0] 中的每一个元素dp[i][0]表示用arr[i]组成面额为0的钱的最少货币数,完全不需要任何货币,即一种方法,初始化为1。

对于剩下的任意dp[i][j],我们依次从左到右,从上到下计算,dp[i][j]的值下面的方法数的和:

  • 完全不用arr[i]的货币,只使用arr[0..i-1]货币时,方法数为dp[i-1][j]
  • 用1张arr[i]货币,剩下的钱用arr[0..i-1]货币组成时,方法数为dp[i-1][j-arr[i]]
  • 用2张arr[i]货币,剩下的钱用arr[0..i-1]货币组成时,方法数为dp[i-1][j-2*arr[i]]
  • 用k张arr[i]货币,剩下的钱用arr[0..i-1]货币组成时,方法数为dp[i-1][j-k*arr[i]]

其实从第二种情况到第k种情况方法的累加值其实就是dp[i][j-arr[i]]的值(解释一下:比如我们有两个硬币[1,2],钱数为5,那么钱数的5的组成方法是可以看作两部分组成,一种是由硬币1单独组成,那么仅有一种情况(1+1+1+1+1);另一种是由1和2共同组成,说明我们的组成方法中至少需要由一个2,所以此时我们先取出一个硬币2,那么我们只要拼出钱数为3即可,这个3还是可以用硬币1和2来拼,所以就相当于求由硬币[1,2]组成的钱数为3的总方法。),所以dp[i][j] = dp[i-1][j] + dp[i][j-arr[i]] 。
作者:小北觅
链接:https://www.jianshu.com/p/4557890d1bbc
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

3.代码实现

class Solution:
    def change(self, amount: int, coins: List[int]) -> int:
        dp = [[0 for _ in range(amount+1)] for _ in range(len(coins) + 1)]
        n = len(coins)
        dp[0][0] = 1
        for i in range(1, n + 1):
            dp[i][0] = 1
            for j in range(1, amount + 1):
                dp[i][j] = dp[i - 1][j]
                if j >= coins[i - 1]:
                    dp[i][j] += dp[i][j - coins[i - 1]]

        # print(dp)
        return dp[-1][-1]

还有一种dfs解法,但是复杂度过高,无法ac,代码如下:

class Solution(object):
    def dfs(self,amount,coins,res,out):
        if amount < 0:
            return
        if amount == 0:
            res[0] += 1
            return
        for coin in coins:
            # 去重,比已有的小的就不能再拿了
            if out and coin < out[-1]:
                continue
            out.append(coin)
            self.dfs(amount-coin,coins,res,out)
            out.pop()

    def change(self, amount, coins):
        """
        :type amount: int
        :type coins: List[int]
        :rtype: int
        """
        res = [0]
        # 排序
        coins.sort()
        self.dfs(amount,coins,res,[])
        return res[0]

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值