322. Coin Change--硬币找零钱问题

本文详细介绍了动态规划在解决硬币找零钱问题中的应用,包括多种情况下的最小货币数和组合数的求解。通过分析题目、设定状态转移方程和编写代码,展示了如何用动态规划解决此类问题。
摘要由CSDN通过智能技术生成

硬币找零钱问题 Coin Change

硬币找零产生的一系列问题,都是属于动态规划问题,所以,在此整理和总结相关的问题,需要自己能更好的掌握动态规划问题。

一、每种面值的货币可以使用任意张,求最小货币数

1. 题目
题目链接: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.

2. 题目分析
给定数组coins,coins中所有的值都是正数且不重复。每个值代表一种面值的硬币,每种面值的货币可以使用任意张,再给定一个整数aim代表要找的钱数,求组成amount的最少货币数。

3. 解题思路
我们用到的样例是面值数组coins=[1, 2, 5], 总额amount = 11;
我们用dp[m][n]来表示所有可能的状态,m表示硬币的种类数,n表示需要凑齐的总额;
则,dp[i][j]表示,用前i(0 <= i <= m)种面值的硬币,去凑齐总额为j(0 <= j <= n),所需要硬币的最少数目。

动态规划,常常适用于有重叠子问题最优子结构性质的问题。若要解一个给定问题,我们需要解其不同部分(即子问题),再通过条件过滤合并子问题的解以得出原问题的最优解。 通常许多子问题非常相似,为此动态规划法试图仅仅解决每个子问题一次,从而减少计算量: 一旦某个给定子问题的解已经算出,则将其记忆化存储,以便下次需要同一个子问题解之时直接查表。 这种做法在重复子问题的数目关于输入的规模呈指数增长时特别有用。所以动态规划就是把大问题逐步分解成容易解决的最基础的小问题,然后通过寻找最基础的问题和大问题的关系,找到大问题的解。

1). 那么我们接下来开始解决最基础的小问题。(注:最基础的小问题其实就是做一些初始化,根据动态规划相关的算法题,由于都是采用备忘录的方式解决算法回溯问题,而备忘录记忆化存储的方式是自下而上分析的,所以必须一开始要有初值,即,我们开始必须进行一些相关的初始化,因为后面的解都是根据前面更小的问题产生的结果做出的选择

  • j=0,就是要凑齐的总额为0,不管使用的硬币种类时,所需要的最小货币数,就是 j 最基本的问题了。很明显,可以是0张2,0张5,0张1,也就是说dp[i][0] = 0 (0 <= i <= n),因为创建数组时,在java中会自动给数组中的每个元素赋初值0,所以一般这个步骤不需要程序猿显示用代码实现;
  • i=0,就是用面值为coins[0]的硬币能凑齐任意总额(小于amount11美元),所需要的最小货币数,就是 i 最基本的问题了。很明显,cosin[0]=1, 1美元需要最少使用1张1美元的硬币,2美元需要最少使用2张1美元的硬币,3美元需要最少使用3张1美元的硬币,,,11美元需要最少使用11张1美元的硬币。

2)找到子问题与大问题的联系,即状态转移方程,要找到状态转移方程,首先要先分析每个子问题产生的情况,然后通过条件筛选找到子问题的最优解,从而能最终找到大问题的最优解。子问题是求解每个dp[i][j],即用前i(0 <= i <= m)种面值的硬币,去凑齐总额为j(0 <= j <= n),从而找到凑齐硬币所需要的最少数目硬币数。
coins[0](i=0)的问题解决了,那么i= 1,2,…n的问题,肯定也是从上面这个问题来的,怎么来的呢?
对于第 i 枚硬币有两种选择:用它来找零 和 不用它找零。

  • 用它来找零:
    dp[i][j] = dp[i][j - coins[i]] + 1; 表示 使用 第 i 枚硬币找零时,对金额为 j 进行找钱所需要的最少硬币数。由于用了第 i 枚硬币,故使用的硬币数量要增1;
  • 不用它来找零:
    dp[i][j] = dp[i - 1][j];表示 不使用第 i 枚硬币找零时(即使用硬币种类0 ~ i-1),对金额为 j 进行找钱所需要的最少硬币数;

总共就这两种可能,我要找最少硬币数量的情况,则这一步的状态转移方程(dp[i][j]的最优解):
dp[i][j] = min(dp[i][j - coins[i]] + 1, dp[i - 1][j]);

4. 代码实现(java)

public static int leastCoinsWithNoLimit(int[] coins, int amount) {
   
        if (coins==null || amount<1){
   
            return 0;
        }
        int[][] dp = new int[coins.length][amount+1];
        for (int j = 1; j <= amount; j++) {
   
            dp[0][j] = Integer.MAX_VALUE;
            if (j>=coins[0] && dp[0][j-coins[0]] != Integer.MAX_VALUE){
   
                dp[0][j] = Math.min(dp[0][j],dp[0][j-coins[0]]+1);
            }
        }

        for (int i = 1; i < coins.length; i++) {
   
            for (int j = 1; j <= amount; j++) {
   
                dp[i][j] = Integer.MAX_VALUE;
                if (j>=coins[i] && dp[i][j-coins[i]] != Integer.MAX_VALUE){
   
                    dp[i][j] = Math.min(dp[i-1]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值