最优硬币组合问题| 豆包MarsCode AI刷题

111 阅读2分钟

一、题目解析

题目描述
小C希望使用最少数量的硬币凑出给定金额 N,并返回对应的硬币组合。硬币面值可以重复使用,但需保证总硬币数量最少。

示例

  • 硬币面值:1,2,5
  • 总金额:18
  • 最优解:5,5,5,2,1(5 枚硬币)。

目标
计算能够凑出总金额 NNN 的最少硬币数量,并返回一种对应的硬币组合。


二、解题思路

该问题是 动态规划 的典型应用,核心思想是用最优子结构解决当前问题。

  1. 状态定义
    定义 dp[i]:凑出金额 i 所需的最少硬币数量。
    定义 combination[i]:凑出金额 i 的硬币组合。

  2. 状态转移
    对于每种硬币面值 coincoincoin,可以转移:

    dp[i]=min⁡(dp[i],dp[i−coin]+1)dp[i] = \min(dp[i], dp[i - coin] + 1)dp[i]=min(dp[i],dp[i−coin]+1)

    如果当前方案更优,则更新对应的硬币组合:

    combination[i]=combination[i−coin]+[coin]combination[i] = combination[i - coin] + [coin]combination[i]=combination[i−coin]+[coin]

  3. 初始化

    • dp[0]=0:凑出金额 0 需要 0 枚硬币。
    • dp[i]=∞:对于其他金额,初始化为一个较大的值表示不可达。
  4. 结果输出
    返回 combination[amount]combination[amount]combination[amount] 作为结果。


三、代码实现

以下是基于动态规划的 Python 实现:

python
复制代码
def solution(coins, amount):
    # 初始化 DP 表
    dp = [float('inf')] * (amount + 1)
    dp[0] = 0
    combination = [[] for _ in range(amount + 1)]

    # 动态规划求解
    for i in range(1, amount + 1):
        for coin in coins:
            if i >= coin and dp[i - coin] + 1 < dp[i]:
                dp[i] = dp[i - coin] + 1
                combination[i] = combination[i - coin] + [coin]

    # 如果无法凑出金额,返回空数组
    return combination[amount] if dp[amount] != float('inf') else []

# 测试用例
if __name__ == "__main__":
    print(solution([1, 2, 5], 18))  # 输出:[5, 5, 5, 2, 1]
    print(solution([2], 3))         # 输出:[](无法凑出金额)
    print(solution([1, 7, 10], 14)) # 输出:[7, 7]

四、知识总结

  1. 动态规划的核心
    本题的关键在于递推公式的设计。通过最优子结构 dp[i]=min⁡(dp[i],dp[i−coin]+1)dp[i] = \min(dp[i], dp[i - coin] + 1)dp[i]=min(dp[i],dp[i−coin]+1),可以逐步找到凑出每个金额的最优解。
  2. 组合记录
    本题需要同时记录最优解对应的硬币组合,结合动态规划表 dp[i] 与 combination[i],可以高效实现。
  3. 边界处理
    如果金额无法凑出,例如硬币面值不能完全覆盖所需金额,需要返回空数组表示不可达。