Leetcode_Dynamic_Programming -- 322. Coin Change [medium]

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, return -1.

硬币找零问题

你有不同面值的硬币,和一个大小amount的总额,写一个函数来计算用最少的硬币来拼凑出这个总额,如果你手中的硬币不能将总额拼出,则返回-1。

Example 1:

Input: coins = [1, 2, 5], amount = 11
Output: 3 
Explanation: 11 = 5 + 5 + 1

Example 2:

Input: coins = [2], amount = 3
Output: -1

Note:

You may assume that you have an infinite number of each kind of coin.

注意:假设你手中每个面值不同的硬币有无限多个

 

题目要求出组成amount的最小硬币数,利用动态规划来做,保留已经算过的值是节省时间的很好的方法,所以我们考虑从0到amount计算所需最小硬币数,维护一个数组dp,数组index是从0开始的,所以dp的长度应该是amount+1,dp[ i ]表示当amount为 i 时,组成 i 所需的最小硬币数,dp[0] = 0。

首先初始化dp数组,我们求解的是最小硬币数,所以我们应考虑用较大的值来初始化数组,硬币的最小数是1,那么我们可以想到,amount最多全由1组成,所以们直接初始化数组中元素为amount+1,这样更新数组就一定能使数组中元素从大变小的去更新了,另外考虑为什么不将数组中元素都初始化为无限大呢,reference中指出在更新的时候存在加一操作,有可能会导致溢出,所以这里将数组中元素初始化为amount+1。

下面需要求解状态转移方程,我们不妨先假设amount=10,cost = [1,2,5],那我们如何能求解组成amount为10的最小硬币数呢?我们用10分别减去cost中元素,得到9、8、5。此时可以发现,如果我们分别知道了9、8、5的硬币最小组合数,然后取他们中最小的组合数,则 dp[10] = 1+min(dp[9],dp[8],dp[5]),此时状态方程就比较明朗了,其实可以有两种写法,分别是:

dp[ i ] = 1 + min( [amount]+[ dp[ i-cost[j] ] for j in cost if i >= cost[j] ] ) 或者 dp[ i ] = min( dp[ i ],dp[ i-cost[ j ] ]+1 ),这两个式子是等价的,式子一就是上述分析的抽象,for循环被加在了状态方程中;式子二则是套在第二层for循环中,可见下面的代码。需要说明的是无论式子一还是式子二都有一个判断 i >= cost[j]的过程,这个是为了保证dp数组在索引时不出现负值索引,只有amount-cost[j]>=0,更新才能持续,另外式子一min函数中加了[amount]是为了防止右边list为空的情况。

 

 

Solutions:

Python

(1)

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        min_count = [amount+1 for i in range(amount+1)]
        min_count[0] = 0
        for i in range(1,amount+1):#从小到大遍历amount,相当于记住从0到amount面值的最小硬币组成数,每一个i都遍历一下coins,得到min_count[i-coins[j]],来看其最小的硬币组成数是多少
            min_count[i] = 1+min([amount]+[min_count[i-j] for j in coins if i-j>=0]) 
                
        if min_count[-1] > amount: #在遍历结束后需要判断coins能否组成amount,如果不能组成,那么min_count中元素会是amount+1,所以只要判断min_count中最后一个元素是否大于amount即可
            return -1
        else:
            return min_count[-1]

(2)与方法一原理相同,状态方程略有差别
class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        min_count = [amount+1 for i in range(amount+1)]
        min_count[0] = 0
        for i in range(1,amount+1):
            for j in coins:
                if i>=j:
                    min_count[i] = min(min_count[i],min_count[i-j]+1) #将方法一的for循环从min()函数中拿出来,其实都一样,min_count从amount+1向下更新

        if min_count[-1] > amount:
            return -1
        else:
            return min_count[-1]

Reference:

https://www.cnblogs.com/grandyang/p/5138186.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值