零钱兑换( LeetCode 322 )

一、题目描述

给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。

计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1

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

示例 1:

输入:coins = [1, 2, 5], amount = 11
输出:3 
解释:11 = 5 + 5 + 1

二、题目解析

三、参考代码

1、Java 代码

class Solution {
    public int coinChange(int[] coins, int amount) {

        // 初始化数组 dp,长度为 amount + 1,因为在 dp 数组中还会存储金额为 0 的情况
        // dp[i] 表示想要凑齐 i 元需要的最少硬币个数
        // dp[0] 表示想要凑齐 0 元需要的最少硬币个数
        // dp[1] 表示想要凑齐 1 元需要的最少硬币个数
        // dp[14] 表示想要凑齐 14 元需要的最少硬币个数
        int[] dp = new int[amount + 1];

        // 首先将数组 dp 里面的值都初始化为 -1
        // -1 表示当前的金额还没有找到需要的最少硬币个数
        Arrays.fill(dp, -1);

        // 想要凑齐 0 元的最少硬币个数是 0 个
        dp[0] = 0;

        // 依次计算想要凑齐 1 元到 amount 的最少硬币个数是多少
        for(int i = 1 ; i <= amount ; i++){

            // 对于每个金额 i 来说,coins 中的每个面值小于 i 的硬币都可以尝试去拼凑 i
            // 比如 i = 8 ,coins 为 [1,2,5,7,10]
            // 其中 1,2,5,7 都小于 8
            // 1 可以尝试去拼凑 8
            // 2 可以尝试去拼凑 8
            // 5 可以尝试去拼凑 8
            // 7 可以尝试去拼凑 8
            // 所以,设置一个变量 j ,遍历数组 coins
            for(int j = 0 ; j < coins.length;j++){

                // 1、如果当前的硬币面值 coins[j] 小于了 i,表示这枚硬币有可能可以拼凑到 i
                // 2、那么 i - coins[j] 表示面值 coins[j] 的硬币想要拼凑 i 需要那些面值的硬币金额
                // 3、而 dp[i-coins[j]] 表示想要凑齐 i - coins[j] 元需要的最少硬币个数
                // 4、如果 dp[i-coins[j]] != -1 ,表示想要凑齐 i - coins[j] 元需要的最少硬币个数有结果
                if(coins[j] <= i && dp[i-coins[j]] != -1){
                    // 这个时候,对于金额 i 来说
                    // 1、如果它之前还没有找到凑齐 i 元需要的最少硬币个数
                    // 2、如果此时计算的最少硬币个数比之前保存的结果 dp[i] 更小
                    // 那么更新 dp[i]
                    if(dp[i] == -1 || dp[i-coins[j]] + 1 < dp[i]){
                        // 更新 dp[i]
                        // dp[i] 表示想要凑齐 i 元需要的最少硬币个数
                        // 这个时候 dp[i] 为获取面值为 j 的那 1 个硬币
                        // 加上获取面值为 i - coins[j] 最少需要 dp[i - coins[j]] 个硬币
                        dp[i] = dp[i - coins[j]] + 1;
                    }

                }
            }
        }

        // dp[amount] 表示想要凑齐 amount 元需要的最少硬币个数
        // 返回这个结果就行
        return dp[amount];
    }
}

2、C++ 代码

class Solution {
public:
    int coinChange(vector<int>& coins, int amount) {
        // 初始化数组 dp,长度为 amount + 1,因为在 dp 数组中还会存储金额为 0 的情况
        // dp[i] 表示想要凑齐 i 元需要的最少硬币个数
        // dp[0] 表示想要凑齐 0 元需要的最少硬币个数
        // dp[1] 表示想要凑齐 1 元需要的最少硬币个数
        // dp[14] 表示想要凑齐 14 元需要的最少硬币个数
        // 首先将数组 dp 里面的值都初始化为 -1
        // -1 表示当前的金额还没有找到需要的最少硬币个数
        vector<int> dp( amount + 1 , -1 );

        // 想要凑齐 0 元的最少硬币个数是 0 个
        dp[0] = 0;

        // 依次计算想要凑齐 1 元到 amount 的最少硬币个数是多少
        for(int i = 1 ; i <= amount ; i++){

            // 对于每个金额 i 来说,coins 中的每个面值小于 i 的硬币都可以尝试去拼凑 i
            // 比如 i = 8 ,coins 为 [1,2,5,7,10]
            // 其中 1,2,5,7 都小于 8
            // 1 可以尝试去拼凑 8
            // 2 可以尝试去拼凑 8
            // 5 可以尝试去拼凑 8
            // 7 可以尝试去拼凑 8
            // 所以,设置一个变量 j ,遍历数组 coins
            for(int j = 0 ; j < coins.size();j++){

                // 1、如果当前的硬币面值 coins[j] 小于了 i,表示这枚硬币有可能可以拼凑到 i
                // 2、那么 i - coins[j] 表示面值 coins[j] 的硬币想要拼凑 i 需要那些面值的硬币金额
                // 3、而 dp[i-coins[j]] 表示想要凑齐 i - coins[j] 元需要的最少硬币个数
                // 4、如果 dp[i-coins[j]] != -1 ,表示想要凑齐 i - coins[j] 元需要的最少硬币个数有结果
                if(coins[j] <= i && dp[i-coins[j]] != -1){
                    // 这个时候,对于金额 i 来说
                    // 1、如果它之前还没有找到凑齐 i 元需要的最少硬币个数
                    // 2、如果此时计算的最少硬币个数比之前保存的结果 dp[i] 更小
                    // 那么更新 dp[i]
                    if(dp[i] == -1 || dp[i-coins[j]] + 1 < dp[i]){
                        // 更新 dp[i]
                        // dp[i] 表示想要凑齐 i 元需要的最少硬币个数
                        // 这个时候 dp[i] 为获取面值为 j 的那 1 个硬币
                        // 加上获取面值为 i - coins[j] 最少需要 dp[i - coins[j]] 个硬币
                        dp[i] = dp[i - coins[j]] + 1;
                    }

                }
            }
        }

        // dp[amount] 表示想要凑齐 amount 元需要的最少硬币个数
        // 返回这个结果就行
        return dp[amount];
    }
};

3、Python 代码

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        # 初始化数组 dp,长度为 amount + 1,因为在 dp 数组中还会存储金额为 0 的情况
        # dp[i] 表示想要凑齐 i 元需要的最少硬币个数
        # dp[0] 表示想要凑齐 0 元需要的最少硬币个数
        # dp[1] 表示想要凑齐 1 元需要的最少硬币个数
        # dp[14] 表示想要凑齐 14 元需要的最少硬币个数
        # 首先将数组 dp 里面的值都初始化为 -1
        # -1 表示当前的金额还没有找到需要的最少硬币个数
        dp = [ -1 for _ in range( amount + 1 )]

        # 想要凑齐 0 元的最少硬币个数是 0 个
        dp[0] = 0

        # 依次计算想要凑齐 1 元到 amount 的最少硬币个数是多少
        for i in range ( 1 , amount + 1 ) :

            # 对于每个金额 i 来说,coins 中的每个面值小于 i 的硬币都可以尝试去拼凑 i
            # 比如 i = 8 ,coins 为 [1,2,5,7,10]
            # 其中 1,2,5,7 都小于 8
            # 1 可以尝试去拼凑 8
            # 2 可以尝试去拼凑 8
            # 5 可以尝试去拼凑 8
            # 7 可以尝试去拼凑 8
            # 所以,设置一个变量 j ,遍历数组 coins
            for j in range( 0 , len(coins) ): 

                # 1、如果当前的硬币面值 coins[j] 小于了 i,表示这枚硬币有可能可以拼凑到 i
                # 2、那么 i - coins[j] 表示面值 coins[j] 的硬币想要拼凑 i 需要那些面值的硬币金额
                # 3、而 dp[i-coins[j]] 表示想要凑齐 i - coins[j] 元需要的最少硬币个数
                # 4、如果 dp[i-coins[j]] != -1 ,表示想要凑齐 i - coins[j] 元需要的最少硬币个数有结果
                if coins[j] <= i and  dp[i-coins[j]] != -1  :
                    # 这个时候,对于金额 i 来说
                    # 1、如果它之前还没有找到凑齐 i 元需要的最少硬币个数
                    # 2、如果此时计算的最少硬币个数比之前保存的结果 dp[i] 更小
                    # 那么更新 dp[i]
                    if dp[i] == -1 or dp[i-coins[j]] + 1 < dp[i] : 
                        # 更新 dp[i]
                        # dp[i] 表示想要凑齐 i 元需要的最少硬币个数
                        # 这个时候 dp[i] 为获取面值为 j 的那 1 个硬币
                        # 加上获取面值为 i - coins[j] 最少需要 dp[i - coins[j]] 个硬币
                        dp[i] = dp[i - coins[j]] + 1

        # dp[amount] 表示想要凑齐 amount 元需要的最少硬币个数
        # 返回这个结果就行
        return dp[amount]

四、动画理解(没有声音)