変更を加える問題 の良い解決策を探していましたが、このコード(Python)を見つけました:
target = 200
coins = [1,2,5,10,20,50,100,200]
ways = [1]+[0]*target
for coin in coins:
for i in range(coin,target+1):
ways[i]+=ways[i-coin]
print(ways[target])
コードが文字通り何をするのか理解するのに問題はありませんが、なぜそれが機能するのか理解できません。誰でも助けることができますか?
要素が「a」または「b」または「c」(私たちのコイン)に等しく、合計が「X」になる可能性のあるすべてのセットを取得するには、次のことができます。
したがって、Xを取得できる方法の数は、X-aとX-bおよびX-cを取得できる方法の数の合計です。
ways[i]+=ways[i-coin]
残りは単純な再発です。
追加のヒント:最初は、合計ゼロを1つの方法で設定できます(空のセット)
ways = [1]+[0]*target
これは 動的計画法 の古典的な例です。キャッシュを使用して、3 + 2 = 5のようなものを2回カウントするという落とし穴を回避します(別の可能な解決策:2 + 3のため)。再帰的アルゴリズムはその落とし穴に陥ります。簡単な例に焦点を当てましょう。ここで、target = 5
およびcoins = [1,2,3]
。あなたが投稿したコードは重要です:
再帰バージョンは重要ですが:
再帰コード:
coinsOptions = [1, 2, 3]
def numberOfWays(target):
if (target < 0):
return 0
Elif(target == 0):
return 1
else:
return sum([numberOfWays(target - coin) for coin in coinsOptions])
print numberOfWays(5)
動的計画法:
target = 5
coins = [1,2,3]
ways = [1]+[0]*target
for coin in coins:
for i in range(coin, target+1):
ways[i]+=ways[i-coin]
print ways[target]
コードの背後にある主なアイデアは次のとおりです。「各ステップで、コインに与えられたways
金額を変更するi
方法があります_[1,...coin]
_」。
したがって、最初の反復では、_1
_の額面のコインしかありません。どのターゲットに対してもこれらのコインだけを使用して変更を加える方法は1つしかないことは明らかだと思います。このステップでは、ways
配列は_[1,...1]
_のようになります(_0
_からtarget
までのすべてのターゲットに対して一方向のみ)。
次のステップでは、前のコインのセットに_2
_の額面のコインを追加します。これで、_0
_からtarget
までの各ターゲットに存在する変更の組み合わせの数を計算できます。明らかに、組み合わせの数は、ターゲット> = _2
_(または一般的な場合は新しいコインが追加された)に対してのみ増加します。したがって、ターゲットが_2
_に等しい場合、組み合わせの数はways[2](old)
+ ways[0](new)
になります。一般に、i
が導入された新しいコインと等しくなるたびに、以前の組み合わせの数に_1
_を追加する必要があります。新しい組み合わせは1つのコインのみで構成されます。
target
> _2
_の場合、回答は「すべてのコインがtarget
未満のcoin
金額のすべての組み合わせ」と「すべてのコインがcoin
未満のcoin
金額のすべての組み合わせ」の合計で構成されます。
ここでは2つの基本的な手順を説明しましたが、簡単に一般化できることを願っています。
_target = 4
_と_coins=[1,2]
_の計算木を示しましょう。
コインを与えられた方法[4] = [1,2] =
コインが与えられた方法[4] = [1] +コインが与えられた方法[2] = [1,2] =
1+ウェイ[2]与えられたコイン= [1] +ウェイ[0]与えられたコイン= [1,2] =
1 + 1 + 1 = 3
したがって、変更を加えるには3つの方法があります:_[1,1,1,1], [1,1,2], [2,2]
_。
上記のコードは、再帰的ソリューションと完全に同等です。 再帰的解決策 を理解していれば、上記の解決策を理解しているに違いありません。
あなたが投稿した解決策は、このコードの要約版です。
/// <summary>
/// We are going to fill the biggest coins one by one.
/// </summary>
/// <param name="n"> the amount of money </param>
public static void MakeChange (int n)
{
int n1, n2, n3; // residual of amount after each coin
int quarter, dime, nickel; // These are number of 25c, 10c, 5c, 1c
for (quarter = n/25; quarter >= 0; quarter--)
{
n1 = n - 25 * quarter;
for (dime = n1/10; dime >= 0; dime--)
{
n2 = n1 - 10 * dime;
for (nickel = n2/5; nickel >= 0 && (n2 - 5*nickel) >= 0; nickel--)
{
n3 = n2 - 5 * nickel;
Console.WriteLine("{0},{1},{2},{3}", quarter, dime, nickel, n3); // n3 becomes the number of cent.
}
}
}
}