ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Leetcode 题解 - 动态规划-0-1 背包(6):硬币找零之二找零钱的硬币数组合

2019-05-07 09:56:09  阅读:352  来源: 互联网

标签:钱数 硬币 int 题解 coins 找零 amount dp


[LeetCode] Coin Change 2 硬币找零之二

 

You are given coins of different denominations and a total amount of money. Write a function to compute the number of combinations that make up that amount. You may assume that you have infinite number of each kind of coin.

Note: You can assume that

  • 0 <= amount <= 5000
  • 1 <= coin <= 5000
  • the number of coins is less than 500
  • the answer is guaranteed to fit into signed 32-bit integer

 

Example 1:

Input: amount = 5, coins = [1, 2, 5]
Output: 4
Explanation: there are four ways to make up the amount:
5=5
5=2+2+1
5=2+1+1+1
5=1+1+1+1+1

 

Example 2:

Input: amount = 3, coins = [2]
Output: 0
Explanation: the amount of 3 cannot be made up just with coins of 2.

 

Example 3:

Input: amount = 10, coins = [10] 
Output: 1

 

这道题是之前那道Coin Change的拓展,那道题问我们最少能用多少个硬币组成给定的钱数,而这道题问的是组成给定钱数总共有多少种不同的方法。那么我们还是要使用DP来做,首先我们来考虑最简单的情况,如果只有一个硬币的话,那么给定钱数的组成方式就最多有1种,就看此钱数能否整除该硬币值。那么当有两个硬币的话,那么组成某个钱数的方式就可能有多种,比如可能由每种硬币单独来组成,或者是两种硬币同时来组成。那么我们怎么量化呢,比如我们有两个硬币[1,2],钱数为5,那么钱数的5的组成方法是可以看作两部分组成,一种是由硬币1单独组成,那么仅有一种情况(1+1+1+1+1);另一种是由1和2共同组成,说明我们的组成方法中至少需要由一个2,所以此时我们先取出一个硬币2,那么我们只要拼出钱数为3即可,这个3还是可以用硬币1和2来拼,所以就相当于求由硬币[1,2]组成的钱数为3的总方法。是不是不太好理解,多想想。那么我们的需要一个二维的dp数组,其中dp[i][j]表示用前i个硬币组成钱数为j的不同组合方法,那么怎么算才不会重复,也不会漏掉呢?我们采用的方法是一个硬币一个硬币的增加,每增加一个硬币,都从1遍历到amount,对于遍历到的当前钱数j,组成方法就是不加上当前硬币的频发dp[i-1][j],还要加上,去掉当前硬币值的钱数的组成方法,当然钱数j要大于当前硬币值,那么我们的递推公式也在上面的分析中得到了:

dp[i][j] = dp[i - 1][j] + (j >= coins[i - 1] ? dp[i][j - coins[i - 1]] : 0)

注意我们要初始化每行的第一个位置为0,参见代码如下:   

比如我们有两个硬币[1,2],钱数为5,那么钱数的5的组成方法是可以看作两部分组成,一种是由硬币1单独组成,那么仅有一种情况(1+1+1+1+1);对应 dp[i-1][j];

另一种是由1和2共同组成,说明我们的组成方法中至少需要由一个2,所以此时我们先取出一个硬币2,那么我们只要拼出钱数为3即可,这个3还是可以用硬币1和2来拼,所以就相当于求由硬币[1,2]组成的钱数为3的总方法。对应dp[i][j-coins[i-1]];

class Solution {
    public int change(int amount, int[] coins) {
        int n = coins.length;
        int[][] dp = new int[n + 1][amount + 1];
//dp[0][0] = 1; dp[i][0] = 1的原因是什么 前几个硬币 组成为0的情况为1 就是都不要
        dp[0][0] = 1;
        for(int i = 1; i <= n; i++ ){
            dp[i][0] = 1;
            for(int j = 1; j <= amount; j++){
                if(j >= coins[i -1 ])
                    dp[i][j] = dp[i-1][j] + dp[i][j - coins[i - 1]];
                else
                    dp[i][j] = dp[i-1][j];
            }
        }
        return dp[n][amount];
        
    }
}

我们可以对空间进行优化,我们发现dp[i][j]仅仅依赖于dp[i - 1][j] 和 dp[i][j - coins[i - 1]] 这两项,那么我们可以使用一个一维dp数组来代替,此时的dp[i]表示组成钱数i的不同方法。其实最开始的时候,博主就想着用一维的dp数组来写,但是博主开始想的方法是把里面两个for循环调换了一个位置,结果计算的种类数要大于正确答案,所以一定要注意for循环的顺序不能搞反,参见代码如下:

coins = [1, 2 ,5]

dp[j] 在没有1 的情况下的j-1  没有2的情况下j-2   j-5把这些情况累加起来就是需要的答案

class Solution {
    public int change(int amount, int[] coins) {
        int n = coins.length;
        int[] dp = new int[amount + 1];
        dp[0] = 1;
        for(int coin : coins){
            for(int j = coin; j <= amount; j++){
                dp[j] += dp[j-coin];
            }
        }
        return dp[amount];  
    }
}

 

标签:钱数,硬币,int,题解,coins,找零,amount,dp
来源: https://blog.csdn.net/qq_40320556/article/details/89913963

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有