Medium

题目描述

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

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

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

示例 1:

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

示例 2:

输入:coins = [2], amount = 3
输出:-1

示例 3:

输入:coins = [1], amount = 0
输出:0

提示:

  • 1 <= coins.length <= 12
  • 1 <= coins[i] <= 2^31 - 1
  • 0 <= amount <= 10^4

解题思路

这是一道经典的动态规划问题,也可以用BFS求解。

动态规划思路(推荐): 定义 dp[i] 表示凑成金额 i 所需的最少硬币数。状态转移方程为: dp[i] = min(dp[i], dp[i - coin] + 1),其中 coin 是硬币面额且 coin <= i

初始状态:dp[0] = 0(凑成0元需要0个硬币),其余位置初始化为一个较大值表示不可达。

BFS思路: 将问题看作图的最短路径问题。从金额0开始,每次尝试加上一个硬币面额,直到达到目标金额。BFS能保证找到的第一个解就是最优解。

贪心思路(不推荐): 优先使用大面额硬币,但这种方法在某些情况下无法得到最优解,比如 coins = [1,3,4], amount = 6,贪心得到 4+1+1=3个硬币,但最优解是 3+3=2个硬币

动态规划方法时间复杂度更好,代码更简洁,是首选解法。

代码实现

class Solution {
public:
    int coinChange(vector<int>& coins, int amount) {
        vector<int> dp(amount + 1, amount + 1);
        dp[0] = 0;
        
        for (int i = 1; i <= amount; i++) {
            for (int coin : coins) {
                if (i >= coin) {
                    dp[i] = min(dp[i], dp[i - coin] + 1);
                }
            }
        }
        
        return dp[amount] > amount ? -1 : dp[amount];
    }
};
class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp = [amount + 1] * (amount + 1)
        dp[0] = 0
        
        for i in range(1, amount + 1):
            for coin in coins:
                if i >= coin:
                    dp[i] = min(dp[i], dp[i - coin] + 1)
        
        return dp[amount] if dp[amount] <= amount else -1
public class Solution {
    public int CoinChange(int[] coins, int amount) {
        int[] dp = new int[amount + 1];
        Array.Fill(dp, amount + 1);
        dp[0] = 0;
        
        for (int i = 1; i <= amount; i++) {
            foreach (int coin in coins) {
                if (i >= coin) {
                    dp[i] = Math.Min(dp[i], dp[i - coin] + 1);
                }
            }
        }
        
        return dp[amount] > amount ? -1 : dp[amount];
    }
}
var coinChange = function(coins, amount) {
    const dp = new Array(amount + 1).fill(amount + 1);
    dp[0] = 0;
    
    for (let i = 1; i <= amount; i++) {
        for (let coin of coins) {
            if (i >= coin) {
                dp[i] = Math.min(dp[i], dp[i - coin] + 1);
            }
        }
    }
    
    return dp[amount] > amount ? -1 : dp[amount];
};

复杂度分析

复杂度类型动态规划解法
时间复杂度O(amount × coins.length)
空间复杂度O(amount)

相关题目