leetcode 322 零钱兑换

目录

  • 1. 题目
  • 2. 解法
    • 2.1. 暴力穷举
    • 2.2. 动态规划-1 自下而上
    • 2.3. 动态规划-2 自上而下
  • 3. 代码及测试结果
    • 3.1. 测试代码:
    • 3.2. 结果:

1. 题目

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

示例 1:
输入: coins = [1, 2, 5], amount = 11
输出: 3
解释: 11 = 5 + 5 + 1

示例 2:
输入: coins = [2], amount = 3
输出: -1
说明:
你可以认为每种硬币的数量是无限的。

2. 解法

2.1. 暴力穷举

暴力穷举实质就是一个个方案试,如果满足条件则记录其使用的硬币数量,再从中取最小值。

2.1.1. 暴力穷举1

注意:并不需要穷尽所有组合,从大数开始组合,求出一个有效组合的硬币数作为最小值过滤器,可以减少无效递归次数;

    def coinChange_1(self, coins, amount):
        """
        暴力穷举
        递归
        def _helper(coins, num, leftamount, cur_coins_list):
        递归函数,coins:硬币池; num:下标; leftamount:剩余钱数; cur_coins_list:当前硬币序列
        时间复杂度:O(S^N);因为最坏情况下每种硬币最多可能有S/Ci个;
        所以可能的组合数为S/C1 * S/C2 * S/C3 ......S/Cn=S^N/C1*C2*C3......Cn
        简化一下就是S^N
        S为金额,N为硬币种数。
        空间复杂度:O(N)最坏情况下,递归的最大深度为N。
        :param coins:
        :param amount:
        :return:
        """
        assert coins is not None and amount > 0, ‘value error.‘
        mincount = amount + 1
        # 反转coins排序方式为大至小
        coins = list(reversed(coins))

        # 递归函数
        def _helper(coins, num, leftamount, cur_coins_list):
            nonlocal mincount
            cur_coins_list.append(coins[num])

            length = len(cur_coins_list)
            if leftamount == 0:
                if length  0:
                if length >= mincount:
                    return
                for i in range(num, len(coins)):
                    _helper(coins, i, leftamount-coins[i], cur_coins_list[:])

        for x in range(len(coins)):
            _helper(coins, x, amount - coins[x], [])

        return -1 if mincount > amount else mincount

2.1.2. 暴力穷举2

同样的逻辑,换个方式。

    def coinChange_1_1(self, coins, amount):
        """
        暴力穷举
        :param coins:
        :param amount:
        :return:
        """
        coins = list(reversed(coins))
        def _helper(index, coins, amount):
            if amount == 0: return 0

            if index  0:
                mincost = float(‘inf‘)
                for i in range(0, amount//coins[index]+1):
                    if amount >= i * coins[index]:
                        res = _helper(index+1, coins, amount - i * coins[index])
                        if res != -1:
                            mincost = min(mincost, res+i)
                return -1 if mincost == float(‘inf‘) else mincost
            return -1
        return _helper(0, coins, amount)

2.2. 动态规划-1 自下而上

定义一维数组dp
dp[i]的值为组合成i时需要的最少硬币数,那么继续向前推就是dp[i]=dp[i-coin[j]] 需要的最少硬币数 +1,+1代表使用coin[j]一次。

原理:

定义数组dp[i][j],i为硬币数量,j为总金额,该处元素定义为如果j可以由coins[0-i]中的硬币组成,则值为组成方案所需硬币数量的最小值。

设dp[x][y]存在解,那么dp[x][y-coin[x]]一定有解(除非y小于x),
而且dp[x][y]的可能值为dp[x][y-coin[x]] + 1 (在前者的基础上多加了一个coin[x])
但是,注意列j代表此金额下的所有解,所以如果dp[x-1][y]存在解,意味着该解对dp[x][y]也是有效的。
题目要求的是解的最小值硬币数,所以二者中取最小值:
dp[x][y] = min(dp[x][y-coin[x]], dp[x-1][y])

当然,这是一个二维数组,但实际中并不关心[x-2]及以前行的数据,而且是逐一向后迭代的,所以可以复用简化为一维数组。

代码:

    def coinChange_2(self, coins, amount):
        """
        动态规划
        从下至上
        :param coins:
        :param amount:
        :return:
        """
        dp = [float(‘inf‘)] * (amount + 1)
        dp[0] = 0

        for coin in coins:
            for x in range(coin, amount + 1):
                dp[x] = min(dp[x], dp[x-coin] + 1)
        return dp[amount] if dp[amount] != float(‘inf‘) else -1

2.3. 动态规划-2 自上而下

递归法的变种,使用数组记录已求过的解。

    def coinChange_2_2(self, coins, amount):
        """
        动态规划/递归穷举/回溯法
        自上而下
        :param coins:
        :param amount:
        :return:
        """
        def _helper(coins, leftamount, count_list):
            if leftamount = 0 and res 

3. 代码及测试结果

3.1. 测试代码:

if __name__ == "__main__":
    # 实例化解决方案类
    so = Solution()

    # 参数设定
    li = [1,2,5,10,20]
    para = (li, 118)

    test_func(so, para)
    pass

3.2. 结果:

 共计有个方法: [‘coinChange_1_1‘, ‘coinChange_1_2‘, ‘coinChange_2_1‘, ‘coinChange_2_2‘]

 ****************************************
方法[1]:coinChange_1_1
说明:暴力穷举
        递归
        def _helper(coins, num, leftamount, cur_coins_list):
        递归函数,coins:硬币池; num:下标; leftamount:剩余钱数; cur_coins_list:当前硬币序列
        时间复杂度:O(S^N);因为最坏情况下每种硬币最多可能有S/Ci个;
        所以可能的组合数为S/C1 * S/C2 * S/C3 ......S/Cn=S^N/C1*C2*C3......Cn
        简化一下就是S^N
        S为金额,N为硬币种数。
        空间复杂度:O(N)最坏情况下,递归的最大深度为N。
        :param coins:
        :param amount:
        :return:
执行结果: 9

 ****************************************
方法[2]:coinChange_1_2
说明:暴力穷举
        :param coins:
        :param amount:
        :return:
执行结果: 9

 ****************************************
方法[3]:coinChange_2_1
说明:动态规划
        从下至上
        :param coins:
        :param amount:
        :return:
执行结果: 9

 ****************************************
方法[4]:coinChange_2_2
说明:动态规划/递归穷举/回溯法
        自上而下
        :param coins:
        :param amount:
        :return:
执行结果: 9

执行时长:
(‘coinChange_1_1‘, 0.0018791932531923659)
(‘coinChange_1_2‘, 0.10514478138982856)
(‘coinChange_2_1‘, 0.00021833724987438408)
(‘coinChange_2_2‘, 0.0006242205989998584)

Process finished with exit code 0

原文地址:https://www.cnblogs.com/wodeboke-y/p/12391282.html

时间: 2024-10-30 13:57:47

leetcode 322 零钱兑换的相关文章

leetcode 322零钱兑换

You are given coins of different denominations and a total amount of money amount. Write a function to compute the fewest number of coins that you need to make up that amount. If that amount of money cannot be made up by any combination of the coins,

[LeetCode]322. 零钱兑换(DP)

题目 给定不同面额的硬币 coins 和一个总金额 amount.编写一个函数来计算可以凑成总金额所需的最少的硬币个数.如果没有任何一种硬币组合能组成总金额,返回?-1. 示例?1: 输入: coins = [1, 2, 5], amount = 11 输出: 3 解释: 11 = 5 + 5 + 1 示例 2: 输入: coins = [2], amount = 3 输出: -1 说明: 你可以认为每种硬币的数量是无限的. 来源:力扣(LeetCode) 链接:https://leetcode

LeetCode:零钱兑换【322】【DP】

LeetCode:零钱兑换[322][DP] 题目描述 给定不同面额的硬币 coins 和一个总金额 amount.编写一个函数来计算可以凑成总金额所需的最少的硬币个数.如果没有任何一种硬币组合能组成总金额,返回 -1. 示例 1: 输入: coins = [1, 2, 5], amount = 11 输出: 3 解释: 11 = 5 + 5 + 1 示例 2: 输入: coins = [2], amount = 3 输出: -1 说明:你可以认为每种硬币的数量是无限的. 题目分析 很显然,这是

322.零钱兑换(动态规划)

给定不同面额的硬币 coins 和一个总金额 amount.编写一个函数来计算可以凑成总金额所需的最少的硬币个数.如果没有任何一种硬币组合能组成总金额,返回 -1. 来源:力扣(LeetCode)链接:https://leetcode-cn.com/problems/coin-change著作权归领扣网络所有.商业转载请联系官方授权,非商业转载请注明出处. 以前我会直接贪婪算法,然后解不出来,现在第一想法就是动态规划:把每一步的最优解都算出来,取最后一项返回. 本题状态转移方程: dp[ i ]

SICP 习题 (2.19) 解题总结:重写零钱兑换程序

SICP 习题2.19 要求我们重新设计1.2.2节的零钱兑换程序,要求我们可以轻易改变程序里用的兑换币种. 我们先看看1.2.2节的零钱兑换程序,代码是这样的: (define (RMB-Change amount) (format #t "Changing ~S~%" amount) (cond ((= amount 0) 0) ((< amount 0) 0) (else (RMB-Change-Recursive amount 1 '() )))) (define (RM

零钱兑换

题目 给定不同面额的硬币 coins 和一个总金额 amount.编写一个函数来计算可以凑成总金额所需的最少的硬币个数.如果没有任何一种硬币组合能组成总金额,返回 -1. 示例 1: 输入: coins = [1, 2, 5], amount = 11 输出: 3 解释: 11 = 5 + 5 + 1 示例 2: 输入: coins = [2], amount = 3 输出: -1 说明: 你可以认为每种硬币的数量是无限的. 代码 思路:动态规划 假设conis={c1,c2,...},那么f(

(Java) LeetCode 322. Coin Change —— 零钱兑换

You are given coins of different denominations and a total amount of money amount. Write a function to compute the fewest number of coins that you need to make up that amount. If that amount of money cannot be made up by any combination of the coins,

LeetCode——零钱兑换

Q:给定不同面额的硬币 coins 和一个总金额 amount.编写一个函数来计算可以凑成总金额所需的最少的硬币个数.如果没有任何一种硬币组合能组成总金额,返回?-1. 示例?1: 输入: coins = [1, 2, 5], amount = 11 输出: 3 解释: 11 = 5 + 5 + 1 示例 2: 输入: coins = [2], amount = 3 输出: -1 说明: 你可以认为每种硬币的数量是无限的. A: 第一反应是回溯我也是没谁了--然后内存就限制了我. public

[Swift]LeetCode322. 零钱兑换 | Coin Change

You are given coins of different denominations and a total amount of money amount. Write a function to compute the fewest number of coins that you need to make up that amount. If that amount of money cannot be made up by any combination of the coins,