Leetcode: 零钱兑换

57 阅读3分钟

原题:leetcode.cn/problems/co…

题目描述

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

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

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

解题思路

  1. 动态规划
    • 定义 dp[i] 表示组成金额 i 所需的最小硬币数
    • 初始化 dp = 0,其他为极大值(表示不可达)
  2. 状态转移
    • 遍历每种金额,对每个硬币面额 coin
    • 若 i >= coin,则 dp[i] = min(dp[i], dp[i-coin] + 1)
  3. 边界处理
    • 金额为0时不需要任何硬币
    • 无法组合的金额返回-1
  4. 完全背包
    • 硬币可重复使用,类似完全背包问题
  5. 自底向上
    • 从小金额向大金额递推求解

关键洞察

  1. 最优子结构
    • 大金额的最优解依赖小金额最优解
  2. 重叠子问题
    • 多个金额计算共享相同子问题
  3. 初始化技巧
    • 极大值设为 amount+1(最大硬币数上限)
  4. 剪枝优化
    • 硬币排序后提前终止无效计算
  5. 负数处理
    • 硬币面额需为正数,金额为正

复杂度分析

指标说明
时间复杂度O(n×amount):n 为硬币种类数
空间复杂度O(amount):dp 数组大小
最优情况O(n×amount):必须填充整个 dp 表
最坏情况O(n×amount):同上

代码实现

  • JavaScript
/**
 * @param {number[]} coins
 * @param {number} amount
 * @return {number}
 */
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(const coin of coins){
            if(coin<=i) {
                dp[i] = Math.min(dp[i],dp[i-coin]+1)
            }
        }
    }
    return dp[amount] === amount+1 ? -1 : dp[amount]
};
  • python
def coinChange(coins, amount):
    dp = [amount + 1] * (amount + 1)
    dp[0] = 0

    for i in range(1, amount + 1):
    for coin in coins:
        if coin <= i:
            dp[i] = min(dp[i], dp[i - coin] + 1)

    return dp[amount] if dp[amount] <= amount else -1
  • rust
pub fn coin_change(coins: Vec<i32>, amount: i32) -> i32 {
    let amount = amount as usize;
    let mut dp = vec![amount + 1; amount + 1];
    dp[0] = 0;

    for i in 1..=amount {
        for &coin in &coins {
            let coin = coin as usize;
            if coin <= i {
                dp[i] = dp[i].min(dp[i - coin] + 1);
            }
        }
    }

    if dp[amount] > amount {
        -1
    } else {
        dp[amount] as i32
    }
}

实际应用场景

  1. 自动售货机:计算最少硬币找零方案`
  2. 金融系统:债券最小拆分单元计算`
  3. 游戏设计:道具合成系统资源最小消耗`
  4. 物流装箱:最小包装箱数量计算`

变种:硬币组合总数

def coinCombinations(coins, amount):
    dp = [0] * (amount + 1)
    dp[0] = 1  # 金额0有1种方案(不取)
    
    for coin in coins:
        for i in range(coin, amount + 1):
            dp[i] += dp[i - coin]
    
    return dp[amount]

相似题目