40. Coin Change
読了目安 約2分
DP / LeetCode 322 (Medium) — 金額を作る最小コイン枚数。作れない場合は -1 を返す。
この章の目次
問題
LeetCode 322. Coin Change(Medium / カテゴリ: DP)
コインの額面の配列 coins と金額 amount が与えられます。
各額面は何枚でも使えます。
amount ちょうどを作る最小のコイン枚数を返し、作れない場合は -1 を返してください。
amount が 0 のときの答えは 0 です。
考え方
ヒント 1
状態を dp[a] = 「金額 a ちょうどを作る最小枚数」と定めます。
貪欲に大きい額面から使う方法は、反例(例: 額面 1, 3, 4 で金額 6)があるので使えません。
ヒント 2
最後に使うコイン c を全額面から選ぶと、遷移は dp[a] = min(dp[a], dp[a - c] + 1)(c <= a のとき)です。
初期条件は dp[0] = 0、残りは「未到達」を表す大きな値にします。
dp[amount] が未到達のままなら -1 を返します。
Swift 実装のポイント
- 「未到達」に
Int.maxを使うと+ 1でオーバーフローして実行時エラーになります。amount + 1を番兵にすれば十分です。 1...amountはamountが 0 のとき実行時エラーになります。stride(from: 1, through: amount, by: 1)なら空になり安全です。- 内側は
for c in coins where c <= aと書くと、配列の範囲外参照を条件で防げます。
模範解答
class Solution {
func coinChange(_ coins: [Int], _ amount: Int) -> Int {
let unreachable = amount + 1
var dp = [Int](repeating: unreachable, count: amount + 1)
dp[0] = 0
for a in stride(from: 1, through: amount, by: 1) {
for c in coins where c <= a {
dp[a] = min(dp[a], dp[a - c] + 1)
}
}
return dp[amount] == unreachable ? -1 : dp[amount]
}
}計算量: O(amount × C)。C は額面の種類数です。