322. Coin Change

322. Coin Change

1. 题目描述
题目链接

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 = [1, 2, 5], amount = 11,既然我要凑11块钱,首先我可以选择面额为1, 2, 5是三种硬币,那么如果我
>对金额11凑硬币数

  • 金额:11
选择面额剩余金额硬币数
1101
291
561

>对金额10,9,6分别继续凑硬币数

  • 金额:10
选择面额剩余金额硬币数
192
282
552
  • 金额:9
选择面额剩余金额硬币数
182
272
542
  • 金额:6
选择面额剩余金额硬币数
152
242
512

>对金额9,8,5分别继续凑硬币数
>对金额8,7,4分别继续凑硬币数
>对金额5,4,1分别继续凑硬币数

直到等于金额,或者发现无法凑齐整数的金额,则结束。
这是自上而下的分析思路,然后从分析过程也发现,很多金额会呗计算多遍,如果9,8,5等等,为了避免不必要的回溯,提高效率,使用空间换时间的方式,使用备忘录法,自下而上计算前面金额所需要的硬币数,然后再使用计算好的金额去计算大金额所需要的硬币数,像这题,我们可以先计算金额为1,2,3,4,10分别所需要的最小硬币数,那么不就直接可以统计出金额为11所需要的最小硬币数了吗?

3. 解决思路

状态dp[value],表示金额为value时,所需要的最小硬币数。那么它会等于dp[value-coins[j]]+1(0=<j<=n)
状态转移方程:dp[value] = Math.min(dp[value], dp[value-coins[j]]+1);

4. 代码实现(java)

package com.algorithm.leetcode.dynamicAllocation;

/**
 * Created by 凌 on 2018/12/15.
 * 描述:322. Coin Change
 */
public class CoinChange {
    public static void main(String[] args) {
//        int[] coins = {1, 2, 5};
//        int amount = 11;
        int[] coins = {2};
        int amount = 0;
        int result = coinChange(coins,amount);
        System.out.println(result);
    }
    public static int coinChange(int[] coins, int amount) {
        if (coins == null || coins.length==0 ){
            return -1;
        }
        //f(n) = min(f(n) , f(n - coins[i])+1 )  ,
        //dp[i]=j表示组成i元,最少需要j个硬币
        int[] dp = new int[amount+1];
        //组成1元,2元,,,n元需要的最小硬币数
        for (int value = 1; value <= amount; value++) {
            //这里必须设置为Integer.MAX_VALUE-1,而不是Integer.MAX_VALUE,因为后面有dp[i - coins[j]] + 1的操作,
            // 如果为MAX_VALUE,+1则为负数最小值
            dp[value] = Integer.MAX_VALUE -1;
            //遍历所有价值的硬币,找到符合组合的硬币
            for (int j = 0; j < coins.length; j++) {
                if (value >= coins[j]){
                    dp[value] = Math.min(dp[value], dp[value-coins[j]]+1);
                }
            }
        }
        return dp[amount] == Integer.MAX_VALUE-1 ? -1 : dp[amount];
    }
}

5. 拓展,求最大硬币数
分析方法同求最小硬币数。

状态dp[value],表示金额为value时,所需要的最大硬币数。那么它会等于dp[value-coins[j]]+1(0=<j<=n)
状态转移方程:dp[value] = Math.max(dp[value], dp[value-coins[j]]+1);

/**
     * 求最大硬币数
     * @param coins
     * @param amount
     * @return
     */
    public static int moreCoins(int[]coins,int amount){
        if (coins==null || coins.length==0 || amount<0){
            return -1;
        }
        //必须注意amount == 0这种特殊情况,因为最后dp[amount] == 0 是直接返回-1,所以需要提前特殊处理
        if (amount == 0){
            return 0;
        }
        //dp[i]=j表示组成i元,最多需要j个硬币
        int[] dp=new int[amount+1];
        for (int i = 1; i <= amount; i++) {
//            dp[i]=Integer.MIN_VALUE+1;
            for (int j = 0; j < coins.length; ++j) {
                if (coins[j] <= i) {
                    dp[i] = Math.max(dp[i], dp[i - coins[j]] + 1);
                }
            }
        }
        return dp[amount] == 0 ? -1 : dp[amount];
    }
```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值