LeetCode LCR 103. 零钱兑换
题目描述


题意分析
每种正面额硬币可以使用任意多枚,求恰好凑出
amount所需的最少枚数;无法凑出时返回 -1。金额为 0 时,不取硬币就是最优方案,答案为 0。总金额不超过 $10^4$,适合按金额保存最优结果。面额可以远大于总金额,这些硬币直接跳过即可。
解法:完全背包求最少硬币
核心思路
[!blue]
定义
dp[j]为仅使用已经处理过的面额、恰好凑出金额j所需的最少硬币数。初始dp[0] = 0,其余金额不可达。所有面额都至少为 1,所以任何凑出j的方案最多用j枚硬币;用amount + 1表示不可达,一定大于所有合法答案,不要求输入中实际存在面额 1。处理面额
coin时,最优方案要么不使用它,保留原来的dp[j];要么至少使用一枚,去掉其中一枚后剩余金额为j - coin,候选枚数就是dp[j - coin] + 1。因此执行dp[j] = min(dp[j], dp[j - coin] + 1)。剩余部分仍允许使用
coin,所以内层金额必须从小到大。更新j时,较小的j - coin已完成本轮处理,能够包含任意多枚当前面额;再加一枚即可覆盖所有使用它的方案。每种面额处理结束后,整张表就保存了允许这些面额时的最优结果。金额循环从
coin开始,保证j - coin非负。若面额超过amount,循环不会执行,也不会访问越界下标。若前驱不可达,候选值为amount + 2,比当前最多为amount + 1的状态更差,取最小值时不会误选它。最后若
dp[amount] > amount,该金额仍不可达,返回 -1;否则返回最少枚数。amount = 0时没有任何转移,初值 0 就是答案。
解题步骤
- 创建长度为
amount + 1的数组,将正金额初始化为不可达,保留dp[0] = 0。- 逐个处理硬币面额
coin。- 从
coin到amount正序更新,比较旧答案与补上一枚硬币的候选答案。- 根据目标状态是否仍不可达,返回 -1 或最少枚数。
代码实现
class Solution {
public int coinChange(int[] coins, int amount) {
int[] dp = new int[amount + 1];
// amount + 1 严格大于任何合法答案,充当「不可达」哨兵且不会溢出。
Arrays.fill(dp, amount + 1);
dp[0] = 0;
for (int coin : coins) {
// 正序遍历,dp[j - coin] 已包含本面额,允许同一面额取多枚。
for (int j = coin; j <= amount; ++j) {
dp[j] = Math.min(dp[j], dp[j - coin] + 1);
}
}
return dp[amount] > amount ? -1 : dp[amount];
}
}
func coinChange(coins []int, amount int) int {
dp := make([]int, amount+1)
// amount + 1 严格大于任何合法答案,充当「不可达」哨兵且不会溢出。
for i := 1; i <= amount; i++ {
dp[i] = amount + 1
}
for _, coin := range coins {
// 正序遍历,dp[j-coin] 已包含本面额,允许同一面额取多枚。
for j := coin; j <= amount; j++ {
dp[j] = min(dp[j], dp[j-coin]+1)
}
}
if dp[amount] > amount {
return -1
}
return dp[amount]
}
复杂度分析
- 时间复杂度:$O(n(A+1))$,其中 $n$ 为面额数、$A$ 为总金额,包含金额为 0 时的面额遍历。
- 空间复杂度:$O(A+1)$,只保存一维金额状态。
关键点总结
[!green]
- 正序读取的是已经允许当前面额的状态,因此可以重复取用;这里与每个位置只能选择一次的背包不同。
- 不可达哨兵应大于真实答案上界,并能安全地参与加一。
- 求最少枚数时取最小值,不累计硬币排列或组合的数量。
易错点总结
[!yellow]
- 将所有状态初始化为 0,会把无法凑出的正金额误认为不需要硬币。
- 容量倒序会限制当前面额只能用一次。
- 用最大整数作哨兵后直接加一,可能发生溢出;本实现使用
amount + 1。- 总是先拿最大面额不能保证最少枚数,必须保留所有已处理面额带来的最优结果。
相似题目
| 题目 | 难度 | 关联与区别 |
|---|---|---|
| 518. 零钱兑换 II | 中等 | 候选面额可重复使用,本题最小化硬币数量,原题累计不区分顺序的组合数。 |
| 279. 完全平方数 | 中等 | 把可选面额限制为完全平方数后,同样求凑成目标所需的最少项数。 |
转载与许可
许可
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!