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

191 阅读3分钟

问题描述

现在有 M 种不同面值的硬币(硬币个数数目无限),如何用这些硬币组合为总数额为 N 的钱,同时满足使用硬币的个数最少?

输入格式

第一行为硬币面值正整数数组 (无序)

第二行为目标总数额,正整数

输出格式

总面值等于目标总数额的,且长度最小的硬币组合数组,结果不要求排序。如果不存在解则返回空数组

解题思路

这是一道经典的动态规划问题,可以把硬币和为dp[i] 表示达到金额 i 所需的最少硬币数。这样对于金额j,对每个硬币面值进行判断,对于特定的硬币面值coin,如果达到j的上一步是取一个coin面值的硬币,那么所需要的硬币数就为dp[i]=dp[i - coin] + 1

  1. 定义状态:我们可以使用一个数组 dp,其中 dp[i] 表示达到金额 i 所需的最少硬币数。
  2. 初始化dp[0] = 0,因为达到金额 0 不需要任何硬币。对于其他金额,初始化为一个较大的值(例如 amount + 1),表示初始状态下无法达到这些金额。
  3. 状态转移:对于每个硬币面值 coin,更新 dp 数组。如果使用当前硬币 coin 可以减少达到金额 i 的硬币数,则更新 dp[i]dp[i] = min(dp[i], dp[i - coin] + 1),其中 coin 是硬币的面值。
  4. 记录路径:遍历每个硬币面值,对于每个面值,从面值开始到目标金额N,更新 dp 数组。为了记录使用的硬币组合,我们可以使用一个数组 usedCoins 来记录每个金额 i 使用的最后一个硬币。
  5. 构建结果 如果 dp[N] 仍然是初始化的较大数,说明没有解决方案。否则,我们可以从 dp[N] 开始逆向构建硬币组合:
  • 从 i = N 开始,逆向遍历到0。
  • 对于每个 i,如果 dp[i] 等于 dp[i - coin] + 1,则将 coin 添加到结果数组中,并将 i 更新为 i - coin

时间复杂度

  • 时间复杂度:O(M * N),其中 M 是硬币种类数,N 是目标金额。
  • 空间复杂度:O(N),用于存储 dp 数组

此题可以衍生出来很多变体

比如完全背包问题

变体:每种硬币的数量有限,问最少需要多少硬币组成目标金额。

解法:使用完全背包问题的动态规划解法。

思路: 定义一个二维数组 dp[i][j],其中 dp[i][j] 表示使用前 i 种硬币组成金额 j 所需的最少硬币数量。初始化 dp[0][j] = ∞(对于所有 j),因为如果没有硬币,我们无法组成任何金额。

状态转移方程

对于每种硬币 i 和每个金额 j(从0到N),我们更新 dp[i][j]

dp[i][j]=min⁡(dp[i][j],dp[i−1][j])dp[i][j]=min(dp[i][j],dp[i−1][j]) dp[i][j]=min⁡(dp[i][j],dp[i−1][j−k×coin[i]]+k)

dp[i][j]=min(dp[i][j],dp[i−1][j−k×coin[i]]+k)

其中 k 是当前硬币 i 可以使用的最大数量,且 k \times coin[i] \leq j