【动态规划】【打卡105天】:剑指 Offer II 103. 最少的硬币数目

1、题目描述

给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。

你可以认为每种硬币的数量是无限的。

2、算法分析

动态规划题目特点:

①计数

        有多少种方式走到右下角;

        有多少种方法选出k个数的和为sum;

②求最大最小值

        从左上角走到右下角的最大数字和;

        最长上升子序列的长度;         

③求存在性

        取石子游戏,先手是否必胜;

        能不能选出k个数使得和是sum;

思路:

①确定状态

        简单的说,需要创建一个数组,数组的每个元素f[i]或者f[i][j]代表什么;一般确定状态最重要的两个意识:最后一步;子问题;

        比如本题,一定有最后一枚硬币:ak;除掉这枚硬币,前面的硬币面值加起来是amount - ak;总金额数 - 最后一个硬币的面值

最后一步:

        我们不关心前面的k-1枚硬币是怎么拼成amount-ak的,我们现在甚至还不知道ak和K,但是我们确定前面的硬币拼出了amount - ak;

        因为是最优策略,所以拼出的amount - ak一定要少。将最后一枚硬币去掉,剩下的k-1的硬币还是最优解。

②转移方程

f[X]含义:X金额,f[X] = M代表的是拼出X的金额需要M枚硬币

        设状态方程f[X] = 最少用多少枚硬币拼出X,则f[X] = min(f[X - coins[i]] + 1,f[X - coins[i+1]] + 1 ....) ,加1代表加上最后一枚硬币。

③计算顺序

初始条件:f[0] = 0;

计算每个amount需要的硬币数,取最小值:f[0],f[1],f[2]....f[amount]

具体看代码注释:

3、代码实现

class Solution {
    // coins:不同金额的的硬币,2,3,5,7
    // amount:总金额
    public int coinChange(int[] coins, int amount) {
        // 需要0....n:[n+1]
        // 需要0....n-1:[n]
        // f[i] = j:i块钱需要j个硬币
        int[] f = new int[amount + 1];
        // 多少种硬币
        int n = coins.length;
        // 初始化数组,0个硬币拼出的是0块钱
        f[0] = 0;
        int i,j;
        // 求用多少硬币分别拼出f[1],f[2],f[3]......f[27]....
        // 遍历
        for(i = 1;i <= amount;i++){
            // 首先假设f[i],第i块钱需要Integer.MAX_VALUE个硬币。后面有f[i]块钱比Integer.MAX_VALUE好,就更改f[i]块钱对应的硬币的值
            f[i] = Integer.MAX_VALUE;
            // 要拼出i块钱,那么最后一枚硬币是什么呢,最后一枚硬币不会跳出coins硬币数组内元素的范围
            // f[i] = min{f[i-coins[0]]+1,f[i-coins[1]]+1,....f[i-coins[n-1]]+1}
            // 遍历coins[] 硬币的数组
            for(j = 0;j < n;j++){
                // i>=coins[j]:总金额大于硬币对应的金额,拼3块钱,最后一枚硬币是5块钱
                // f[i - coins[j]] != Integer.MAX_VALUE:防止数组越界
                if(i >= coins[j] && f[i - coins[j]] != Integer.MAX_VALUE){
                    // 比较f[i]对应的硬币数和 去掉最后一个硬币f[i-coins[j]]+1对应的硬币数。。比较出最小的
                    f[i] = Math.min(f[i],f[i - coins[j]] + 1);
                }
            }
        }
        // 如果拼不出来f[i]的值一直是没有改变的
        if(f[amount] == Integer.MAX_VALUE){
            return -1;
        }
        // 返回f[amount]
        return f[amount];
    }
}

Guess you like

Origin blog.csdn.net/Sunshineoe/article/details/121499058