D

当前:LC322 · 零钱兑换 · 首次出现于 Day 37 · 路径:顶栏「56天打卡」→ 点击 LC 题号 → 逐题动画

LC322

零钱兑换

贪心收银员的陷阱:拿最大面额 ≠ 用最少硬币

人人都会背 dp[a]=min(dp[a-c]+1),却很少有人能讲清贪心为什么会错。这里把它做成可玩的:你来决定每个金额「最后一枚硬币剥哪个」,即时看到命中、连击与星级;再用反例沙盒亲手让贪心翻车。

完全背包 · 最值时间 O(amount·coins)空间 O(amount)
先别背公式 · 贪心为什么会翻车
贪心:拿最大面额
4 + 1 + 1
3 枚
最优:DP 枚举
3 + 3
2 枚

coins=[1, 3, 4]6:最大的一步会把余额推到一个「难凑」的数。 要稳拿最优,必须站在每个金额上,枚举「最后一枚硬币是哪个」,取最小 —— 这就是 DP。

命中 0%0/0 连击 0 最佳 0
填 dp[1] / dp[6]
0
0
1
?
a
2
·
3
·
4
·
5
·
6
·

绿 已定稿 · 当前 a · 候选会跳回的子问题 · ∞ 暂不可达 · · 尚未计算

凑出 1 元,最后一枚硬币剥哪个最省?
设计哲学 · 为什么真实生活里贪心找零没问题?

真实货币 [1, 5, 10, 25] 是「标准币制(canonical)」—— 数学上能证明贪心拿最大面额永远等于最优,所以收银员从不出错。但面试给的是 任意面额,比如 [1,3,4],它不是标准币制,贪心立刻翻车。 这就是为什么这题默认必须上 DP:你不能赌币制刚好是标准的

LC279 完全平方数 —— 面额=平方数的同款完全背包最值LC518 零钱兑换 II —— 求组合数(遍历顺序不同)LC377 组合总和 Ⅳ —— 求排列数

不变量 · 算法为什么对

  • · dp[a] = 凑出 a 的最少硬币数;dp[0] = 0 是地基。
  • · 转移 dp[a] = 1 + min(dp[a−c]),枚举每一枚硬币 c ≤ a。
  • · 不可达保持 ∞(代码用 amount+1 哨兵),最后判 > amount 返回 -1。
  • · 完全背包:金额正序遍历,每种硬币可重复取用。

易错点 · 面试官最爱追问

  • 贪心拿最大面额不一定最优 —— coins=[1,3,4] 凑 6:贪心 4+1+1=3 枚,最优 3+3=2 枚。最大的一步会把余额推到难凑的数。
  • 不可达要用哨兵,别直接返回 dp[amount] —— 凑不出时 dp[amount] 还是 ∞(代码里是 amount+1 上界)。最后要判 dp[amount] > amount 才返回 -1。
  • 完全背包:金额正序遍历 —— 每种硬币可重复使用,金额从小到大正序刷新;别和 0/1 背包的倒序搞混。
  • 求最少枚数,不是方案数 —— 这题是最值 min;求「有几种凑法」是 LC518,遍历顺序和转移都不同。
  • · 1 ≤ coins.length,面额各不相同且可无限取用
  • · 0 ≤ amount ≤ 10^4
  • · dp[0] = 0 作为递推地基
  • · 凑不出返回 -1(dp[amount] 仍是 ∞)