Leetcode刷题记录 day9

每日一题 零钱兑换 II

给你一个整数数组 coins 表示不同面额的硬币,另给一个整数 amount 表示总金额。

请你计算并返回可以凑成总金额的硬币组合数。如果任何硬币组合都无法凑出总金额,返回 0 。

假设每一种面额的硬币有无限个。 

题目数据保证结果符合 32 位带符号整数。

解题思路

是昨天写的零钱兑换的后续,但是这次需要的是计算并返回所有可行的组合数。还是使用动态规划的思路

使用一个dp数组储存金额为i,i \in (0, amount)的组合数,没有组合能凑成0,因此dp[0]=0。

我们遍历coins数组中不同面额的硬币coin,若金额“i”比硬币的任何一个面额都要小,那它必然不能被组合出来,即dp[i]=0,所以我们只需要关注i \in (coin, amount)的金额。

若金额“i-coin"是一个可被组合出来的金额数,就说明金额“i"的也可被组合(两者之间差一个coin的面额),因此可以使用dp[i-coin]的值更新dp[i]:dp[i] += dp[i-coin]

但当i==coin时,也至少有一个组合为一枚面额为coin的硬币,为方便计算,我们将dp[0]初始化为1。

如coins[2, 3],amount=5,流程如下:

i = 2

dp[2] += dp[0]                # dp[2] = 1

dp[3] += dp[1]                # dp[1]还未被计算,初始化为0,dp[3] = 0

dp[4] += dp[2]                # dp[4] = 1

dp[5] += dp[3]                # dp[5] = 0

i = 3

dp[3] += dp[0]                # dp[3] = 1

dp[4] += dp[1]                # dp[4] = 1

dp[5] += dp[2]                # dp[5] = 0

通过上述计算过程我们便得到了amount为1到5的所有组合数,并储存在了dp数组的对应位置。

第二种实现方法是使用记忆化搜索。

我们可以定义了一个内部递归函数dfs来计算硬币组合数,使用备忘录memo来存储已经计算过的结果,避免重复计算。递归函数中,我们可以选择包含当前硬币和不包含当前硬币两种情况,然后将结果累加并返回。

代码实现

class Solution:
    def change(self, amount: int, coins: List[int]) -> int:
        dp = [0] * (amount + 1)
        dp[0] = 1
        for coin in coins:
            for i in range(coin, amount + 1):
                dp[i] += dp[i-coin]
        return dp[amount]
def change(self, amount: int, coins: List[int]) -> int:
        memo = {}
        def memery(amount, index):
            if amount == 0:
                return 1
            if amount < 0 or index == len(coins):
                return 0
            if (amount, index) in memo:
                return memo[(amount, index)]
            
            include_ = memery(amount-coins[index], index)
            disinclude_ = memery(amount, index + 1)
            memo[(amount, index)] = include_ + disinclude_
            return memo[(amount, index)]
        return memery(amount, 0)

复杂度分析

动态规划:时间O(n*amount),其中n是硬币数组的长度,空间O(amount)

记忆化搜索:时间O(n*amount), 空间O(n*amount)

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值