タイトル説明
https://leetcode-cn.com/problems/coin-change-2/
解決
完全なナップサック問題、フレーム:
- 状態の決定は、問題の変数です—1。さまざまな種類のコイン; 2。合計金額; 3。使用されたコインの数;次に、問題に応じて、最初の2つの数量を使用して現在の金額を表すだけです。問題、dp配列が2次元であり、2つの次元がそれぞれ変数1と2に対応することを示します。
- dp配列の定義:dp [i] [j]は、最初のiコインがjの量を構成できる方法の数を表します。この定義を厳密に信じてください。
- 再発プロセスを決定します。i番目のコインをロードできる場合は、インストールしないことを選択し、次にdp [i] [j] = dp [i-1] [j]、インストールすることを選択し、次にp [i] [j] = dp [i] [j-coins [i-1]]なので、この場合はdp [i] [j] = dp [i-1] [j] + dp [i] [j-conis [ i-1]];収まらない場合は、dp [i] [j] = d [i-1] [j]
- 再帰が成功しない場合は、2番目のステップに戻り、dp配列の定義が間違っています。dp配列の定義を再考してください。
- 境界の状況を考えてみましょう。dp[i] [0]は金額を0にする方法であり、収集しない方法は1つだけです。質問の結果は1、dp [0] [j]です。 = 0は、収集する金貨がないことを意味します。
最後に、コードを記述します。
class Solution {
public int change(int amount, int[] coins) {
if(amount==0) return 1;
if( coins==null || coins.length==0) return 0;
int [][]dp = new int[coins.length+1][amount+1];//表示前i种硬币组合为j元的可能方法数
//边界 dp[0][j]=0 dp[i][0]=1
for(int i=0;i<=coins.length;i++){
dp[i][0] = 1;
}
//递推方程
for(int i=1;i<=coins.length;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[coins.length][amount];
}
}
次に、ソリューションから、dp [i] [j]が前の2つの状態に関連しており、状態の圧縮を使用してスペースの複雑さを軽減できることがわかります。
- 1次元配列dp [j]
class Solution {
public int change(int amount, int[] coins) {
if(amount==0) return 1;
if( coins==null || coins.length==0) return 0;
int []dp = new int[amount+1];//表示前i种硬币组合为j元的可能方法数
//边界 dp[0][j]=0 dp[i][0]=1
dp[0] = 1;
//递推方程
for(int i=1;i<=coins.length;i++){
for(int j=1;j<=amount;j++){
if(j-coins[i-1]>=0){
//当前硬币装得下
dp[j] = dp[j] //不装
+ dp[j-coins[i-1]];//装下该硬币,但是因为数量无限个,可以继续装
}
// }else//装不下
// dp[j] = dp[j];
}
}
return dp[amount];
}
}