一、题目解析
题目描述
小C希望使用最少数量的硬币凑出给定金额 N,并返回对应的硬币组合。硬币面值可以重复使用,但需保证总硬币数量最少。
示例
- 硬币面值:1,2,5
- 总金额:18
- 最优解:5,5,5,2,1(5 枚硬币)。
目标
计算能够凑出总金额 NNN 的最少硬币数量,并返回一种对应的硬币组合。
二、解题思路
该问题是 动态规划 的典型应用,核心思想是用最优子结构解决当前问题。
-
状态定义
定义 dp[i]:凑出金额 i 所需的最少硬币数量。
定义 combination[i]:凑出金额 i 的硬币组合。 -
状态转移
对于每种硬币面值 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]
-
初始化
- dp[0]=0:凑出金额 0 需要 0 枚硬币。
- dp[i]=∞:对于其他金额,初始化为一个较大的值表示不可达。
-
结果输出
返回 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]
四、知识总结
- 动态规划的核心
本题的关键在于递推公式的设计。通过最优子结构 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),可以逐步找到凑出每个金额的最优解。 - 组合记录
本题需要同时记录最优解对应的硬币组合,结合动态规划表 dp[i] 与 combination[i],可以高效实现。 - 边界处理
如果金额无法凑出,例如硬币面值不能完全覆盖所需金额,需要返回空数组表示不可达。