DP
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| class Solution { public int change(int amount, int[] coins) { int[][] dp = new int[coins.length + 1][amount + 1];
for (int i = 0; i < dp.length; i++) { dp[i][0] = 1; }
for (int i = 1; i <= coins.length; i++) { for (int j = 1; j <= amount; j++) { for (int k = 0; j - k * coins[i - 1] >= 0; k++) { dp[i][j] += dp[i - 1][j - k * coins[i - 1]]; } } }
return dp[coins.length][amount]; } }
|
DP
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| class Solution { public int change(int amount, int[] coins) { int[][] dp = new int[coins.length + 1][amount + 1];
dp[0][0] = 1;
for (int j = 1; j <= coins.length; j++) { dp[j][0] = 1; }
for (int i = 1; i <= coins.length; i++) { for (int j = 1; j <= amount; j++) { dp[i][j] = dp[i - 1][j]; if (j - coins[i - 1] >= 0) { dp[i][j] += dp[i][j - coins[i - 1]]; } } }
return dp[coins.length][amount]; } }
|
设第 i 个硬币的价值为 wi,设我们最多可以使用 k 次该硬币:
- 当不使用第 i 个硬币时,dp[i][j] = dp[i - 1][j]
- 当使用 1 次第 i 个硬币时,dp[i][j] = dp[i - 1][j - wi]
- 当使用 2 次第 i 个硬币时,dp[i][j] = dp[i - 1][j - 2 * wi]
- 当使用 k 次第 i 个硬币时,dp[i][j] = dp[i - 1][j - k * wi]
易知:dp[i][j] = sum(dp[i - 1][j], dp[i - 1][j - wi], dp[i - 1][j - 2 * wi], … dp[i - 1][j - k * wi]) ①
令 j = j - wi,因为少了一个 wi,所以能够使用的最大硬币数为 k - 1:
易知:dp[i][j - wi] = sum(dp[i - 1][j - wi], dp[i - 1][j - 2 * wi], dp[i - 1][j - 3 * wi], … dp[i - 1][j - wi - (k - 1) * wi])
化简:dp[i][j - wi] = sum(dp[i - 1][j - wi], dp[i - 1][j - 2 * wi], dp[i - 1][j - 3 * wi], … dp[i - 1][j - k * wi]) ②
将 ② 式替换 ① 式中的部分项后得到化简后的 ① 式:
dp[i][j] = sum(dp[i - 1][j], dp[i][j - wi])
DP
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| class Solution { public int change(int amount, int[] coins) { int[] dp = new int[amount + 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 - coins[i - 1]]; } } }
return dp[amount]; } }
|
DP
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| class Solution { public int change(int amount, int[] coins) { int[] dp = new int[amount + 1]; dp[0] = 1;
for (int coin : coins) { for (int i = coin; i <= amount; i++) { dp[i] += dp[i - coin]; } }
return dp[amount]; } }
|
示例:amount = 4, coins = [1, 2], 第一层循环完成时:
dp[0] = 1, 组合:[]
dp[1] = dp[0] = 1, 组合:[1]
dp[2] = dp[1] = 1, 组合:[1, 1]
dp[3] = dp[2] = 1, 组合:[1, 1, 1]
dp[4] = dp[3] = 1, 组合:[1, 1, 1, 1]
第二层循环完成时:
dp[0] = 1, 组合:[]
dp[1] = 1, 组合:[1]
dp[2] = dp[2] + dp[0] = 2, 组合:[1, 1], [2]
dp[3] = dp[3] + dp[1] = 2, 组合:[1, 1, 1], [1, 2]
dp[4] = dp[4] + dp[2], 组合:[1, 1, 1, 1], [1, 1, 2], [2, 2]
coin 在外层循环保证了不会出现 [2, 1, 2] 这样的组合,即不会出现与 [1, 1, 2] 相同的组合。
References
518. Coin Change 2
『 一文搞懂完全背包 』从0-1背包到完全背包,逐层深入+数学推导