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

Reference Answer

思路分析

对于求极值问题,我们还是主要考虑动态规划Dynamic Programming来做,好处是保留了一些中间状态的计算值,这样可以避免大量的重复计算。我们维护一个一维动态数组dp,其中dp[i]表示钱数为i时的最小硬币数的找零,注意由于数组是从0开始的,所以我们要多申请一位,数组大小为amount+1,这样最终结果就可以保存在dp[amount]中了。初始化dp[0] = 0,因为目标值若为0时,就不需要硬币了。其他值可以初始化为整型最大值,或者是amount+1,为啥呢,因为最小的硬币是1,所以amount最多需要amount个硬币,amount+1也就相当于整型最大值的作用了。好,接下来就是要找状态转移方程了,没思路?不要紧!回归例子1,假设我取了一个值为5的硬币,那么由于目标值是11,所以是不是假如我们知道dp[6],那么就知道了组成11的dp值了?所以我们更新dp[i]的方法就是遍历每个硬币,如果遍历到的硬币值小于i值(比如我们不能用值为5的硬币去更新dp[3])时,我们用 dp[i - coins[j]] + 1 来更新dp[i],所以状态转移方程为:dp[i] = min(dp[i], dp[i - coins[j]] + 1);
其中coins[j]为第j个硬币,而i - coins[j]为钱数i减去其中一个硬币的值,剩余的钱数在dp数组中找到值,然后加1和当前dp数组中的值做比较,取较小的那个更新dp数组。先来看迭代的写法如下所示:

Code(超时)

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

C++ Version:

class Solution {
public:
    int coinChange(vector<int>& coins, int amount) {
        int INF = amount + 1;
        vector<int> dp(amount+1, INF);
        dp[0] = 0;
        for (auto c:coins){
            for (int i = c; i <= amount; ++i){
                dp[i] = min(dp[i], dp[i-c] + 1);
            }
        }
        return dp[amount] < INF ? dp[amount] : -1;
        
    }
};

优化版:

class Solution:
    def coinChange(self, coins, amount):
        """
        :type coins: List[int]
        :type amount: int
        :rtype: int
        """
        dp = [0] + [-1] * amount
        for x in range(amount):
            if dp[x] < 0:
                continue
            for coin in coins:
                if x + coin > amount:
                    continue
                if dp[x+coin] < 0 or dp[x+coin] > dp[x] + 1:
                    dp[x+coin] = dp[x] + 1
        return dp[amount]

Note

  • 还是DP掌握地太差了,不能找出规律,仔细体会参考答案的状态转移方程 dp[i] = min(dp[i], dp[i - coin] + 1)
  • 将目标作为 dp 列表长度,钱作为索引,需要硬币数作为对应索引值,这种思路在DP中几乎是通用求解路线。

参考文献

[1] https://blog.csdn.net/fuxuemingzhu/article/details/83592442
[2] https://blog.csdn.net/asd136912/article/details/79080693
[3] http://www.cnblogs.com/grandyang/p/5138186.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值