動的計画法 (DP) でおつりの硬貨の枚数を最も少なくする

何種類かの​硬貨を​何枚でも​使える​とき、​ちょうど​ある​金額を​作るのに​要る​最も​少ない​枚数を​求める。​大きい​硬貨から​取る​欲張りは​外れる​ことが​あり、​小さい​金額の​答えを​順に​積み上げる​ DP で​解きます。

海外で定番の問題リスト 4 本のうち 4 本に入っています (Blind 75、NeetCode 150、Grind 75、LeetCode Top Interview 150)

問題

自動販売機のおつりを考えます。硬貨の額面の並び coins と、払いたい金額 amount を受け取ります。どの硬貨も何枚でも使えるとき、合計がちょうど amount になる硬貨の枚数の最小を返す関数を書いてください。どう組み合わせても作れないときは -1 を返します。

  • amount が 0 のときは、硬貨を 1 枚も使わないので 0 を返します
  • coins の長さは 1 以上 12 以下で、額面はどれも違います。額面は 1 以上 2147483647 以下です
  • amount は 0 以上 10000 以下です

入力と出力の例

例 1

10 円 2 枚、5 円 1 枚、1 円 2 枚の 5 枚です。

入力

coins = [1, 5, 10]
amount = 27

出力

5

例 2

大きい 4 から取ると 4、1、1 の 3 枚ですが、3 を 2 枚にすれば 2 枚で済みます。

入力

coins = [1, 3, 4]
amount = 6

出力

2

例 3

4 と 6 はどちらも偶数なので、奇数の 9 は作れません。

入力

coins = [4, 6]
amount = 9

出力

-1

面接での聞かれ方

面接では、短い一文で渡されることが多いです。

  • 「硬貨の種類と金額があります。最小の枚数を返してください。作れなければ -1 です」
  • 最初に欲張り (大きい硬貨から取る) を書くと、「4、3、1 で 6 を作ると?」と反例を出されます。欲張りが外れることに自分で気づけるかを見ています
  • 書き終えたあとに「再帰とメモで書けますか」「表を作る向きはどちらでもいいですか」「使う硬貨の内訳も返せますか」と続けて聞かれます

金額 0 のときに何を返すか、作れないときに何を返すかは、先に確かめておきます。日本の硬貨のように欲張りで必ず最小になる組み合わせもありますが、この問題では額面が自由なので当てにできません。

アルゴリズムのチートシートの「動的計画法 (DP)」に、​この​型の​要点を​表と​コードで​まとめています。

main.py
def min_coins(coins, amount):
    # ここに書きます
    pass


# 「実行」を押すと、この行の結果が出ます
print(min_coins([1, 5, 10], 27))

できているか「例で確かめる」を押すと、問題の例を全部この関数に渡して、通ったかをここに出します