零钱兑换(求最少的硬币个数)

        给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1

        你可以认为每种硬币的数量是无限的。

示例 1:

输入:coins = [1, 2, 5], amount = 11

输出:3
 
解释:11 = 5 + 5 + 1

示例 2:

输入:coins = [2], amount = 3

输出:-1

示例 3:

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

示例 4:

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

示例 5:

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

提示:

  • 1 <= coins.length <= 12
  • 1 <= coins[i] <= 2^{31}-1
  • 0 <= amount <= 10^{4}

示例代码1: 【dp数组的迭代解法】

class Solution(object):
    def coinChange(self, coins, amount):
        """
        :type coins: List[int]
        :type amount: int
        :rtype: int
        """
        dp = [0] + [10001] * amount
        for coin in coins:
            for i in range(coin, amount+1):
                dp[i] = min(dp[i], dp[i-coin]+1)
        return dp[-1] if dp[-1] != 10001 else -1

思路解析:

示例代码2:【dp数组的迭代解法】

class Solution(object):
    def coinChange(self, coins, amount):
        dp = [0] + [float('inf')] * amount
        for coin in coins:
            for i in range(coin, amount + 1):
                dp[i] = min(dp[i], dp[i - coin] + 1)
        return dp[-1] if dp[-1] != float("inf") else -1


coins = [1, 2, 5]
amount = 11
obj = Solution()
ret = obj.coinChange(coins, amount)
print(ret)

 示例代码3: 【递归】

class Solution(object):
    def coinChange(self, coins, amount):
        def dp(n):
            if n == 0:
                return 0
            if n < 0:
                return -1
            res = float("inf")
            for coin in coins:
                subproblem = dp(n - coin)
                # ⼦问题⽆解,跳过
                if subproblem == -1:
                    continue
                res = min(res, subproblem + 1)
            return res if res != float("inf") else -1

        return dp(amount)


coins = [1, 2, 5]
amount = 11
obj = Solution()
ret = obj.coinChange(coins, amount)
print(ret)

 思路解析:

        此时算法的时间复杂度较大,在力扣上提交是超时的。时间复杂度为:O(n^{k}).

暴⼒解法就是遍历⼀棵 N 叉树:

示例代码4:  【带备忘录的递归】

class Solution(object):
    def coinChange(self, coins, amount):
        #  备忘录
        memo = dict()

        def dp(n):
            #  查看备忘录,避免重复计算
            if n in memo:
                return memo[n]
            if n == 0:
                return 0
            if n < 0:
                return -1
            res = float("inf")
            for coin in coins:
                subproblem = dp(n - coin)
                # ⼦问题⽆解,跳过
                if subproblem == -1:
                    continue
                res = min(res, subproblem + 1)
            #  记录备忘录
            memo[n] = res if res != float("inf") else -1
            return memo[n]

        return dp(amount)


coins = [1, 2, 5]
amount = 11
obj = Solution()
ret = obj.coinChange(coins, amount)
print(ret)

 思路解析:

        耗时的原因是重复计算, 那么可以造⼀个「备忘录」,每次算出某个⼦问题的答案后别急着返 回,先记到「备忘录」⾥再返回;每次遇到⼀个⼦问题先去「备忘录」⾥查 ⼀查,如果发现之前已经解决过这个问题了,直接把答案拿出来⽤,不要再 耗时去计算了。

        很显然「备忘录」⼤⼤减⼩了⼦问题数⽬,完全消除了⼦问题的 冗余,所以⼦问题总数不会超过⾦额数 n,即⼦问题数⽬为 O(n)。处理⼀个 ⼦问题的时间不变,仍是 O(k),所以总的时间复杂度是 O(kn)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值