異なる金種
coins
と合計金額のコインが与えられamount
ます。合計金額を構成できるコインを計算する関数を記述します组合数
。各宗派の無限のコインがあると仮定します。
この質問は0-1
、完全なナップサック問題であるナップサック問題の変形です。完全なナップサック問題とは何ですか?
0-1バックパックの問題をもう一度確認してみましょう。1の重量でロードできるW
バックパックとN
アイテム(各アイテムは異なります)を提供します。各アイテムには、重量と値の2つの属性があります。i
重量最初の項目があるwt[i]
と値があるval[i]
。今、あなたがアイテムをパックするために、このバックパックを使用してみましょう、ロードすることができる最大値は何ですか?
0-1
バックパック問題のアイテムの数は限られています。正確には、各アイテムは一意であり、ここには各金種のコインが無限にあります。つまり完全背包问题
、アイデアは0-1バックパックのアイデアと同じです。しかし、状態遷移方程式はわずかに変更されています。
私たち0-1
はバックパックのアイデアに従ってこの問題を解決することができます:
異なる金種のコインを0-1
ナップサック問題のアイテムと比較し、合計金額をナップサックの容量と比較して、解決のためのナップサック問題モデルに変換できるようにします。
- 最初のステップ:「ステータス」と「選択」をクリアする
「バックパックの容量」と「オプションアイテム」の2つの状態があり、「バックパックに詰める」または「バックパックに入れない」のどちらかを選択できます。
for 状态1 in 状态1的所有取值:
for 状态2 in 状态2的所有取值:
for ...
dp[状态1][状态2][...] = 计算(选择1,选择2...)
- 配列の明確な定義
dp
dp[i][j]
次のように定義されます:i
バックパックを埋める方法j
がある場合のバックパックの容量の場合、最初の2つの項目のみdp[i][j]
。
コインチェンジャーモデルに変換すると、coins
フロントi
コインの額面のみを使用します。Couchuの金額が必要な場合はj
、dp[i][j]
さまざまなエラー方法があります。
基本ケースはdp[0][..] = 0, dp[..][0] = 1
です。なぜなら、コインの額面を使わないと、金額を補うことができないからです。目標額が0の場合、「何もしないでルールを決める」ことが唯一の補い方です。
私たちの究極の目標は、見つけることですdp[N][amount]
これは、N
あるcoins
配列のサイズ。
- 「選択」によると、状態遷移の論理について考えてください
coins[i]
コインの額面を使用しない場合、Couchuの金種はj
いくつかの方法dp[i][j]
が等しくなければならずdp[i-1][j]
、以前の結果を継承します。
coins[i]
コインの額面を使用する場合、それdp[i][j]
は等しくなければなりませんdp[i][j-coins[i-1]]
。
まず、i
最初からそのままなのでcoins
、インデックスはi-1
時間でi
コインの額面を表します。
dp[i][j-coins[i-1]]
このコインを使用する場合は、金額の回収方法に注意する必要があることを理解するのは難しいことではありませんj - coins[i-1]
。
合計すると2つのオプションでありdp[i][j]
、「種の総数とエラー」を尋ねたいのでdp[i][j]
、値は2つのオプションの結果の合計よりも大きくする必要があります。
class Solution {
int change(int amount, int[] coins) {
int n = coins.length;
int[][] dp = amount int[n + 1][amount + 1];
for (int i = 0; i <= n; i++) {
dp[i][0] = 1;
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= amount; j++){
if (j - coins[i-1] >= 0){
dp[i][j] = dp[i - 1][j] + dp[i][j - coins[i-1]];
}else {
dp[i][j] = dp[i - 1][j];
}
}
}
return dp[n][amount];
}
}
また、当社は、観察により発見することができdp
、配列とのみ転送するdp[i][..]
とdp[i-1][..]
、関連します:
したがって、状態を圧縮して、アルゴリズムのスペースの複雑さをさらに減らすことができます。
class Solution {
public int change(int amount, int[] coins) {
int n = coins.length;
int[] dp = new int[amount + 1];
dp[0] = 1;
for (int i = 0; i < n; i++){
for (int j = 1; j <= amount; j++){
if (j - coins[i] >= 0){
dp[j] += dp[j-coins[i]];
}
}
}
return dp[amount];
}
}
それもこれを行うことができます:
class Solution {
public int change(int amount, int[] coins) {
int[] dp = new int[amount + 1];
dp[0] = 1;
for (int coin : coins) {
// j直接从coin开始,避免了j - coins[i] >= 0的判断
for (int j = coin; j <= amount; j++) {
dp[j] += dp[j - coin];
}
}
return dp[amount];
}
}
時間計算量O(N * amount)
スペースの複雑さO(量)