Poison

518. Coin Change 2

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) {
// 定义 dp[i][j] 为前 i 个硬币,使用其中一些硬币可以凑成总金额 j 的组合数
int[][] dp = new int[coins.length + 1][amount + 1];

// 金额为 0 时前 i 个硬币,选择 0 个硬币均能组成,此时组合数为 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) {
// 定义 dp[i][j] 为前 i 个硬币中使用一些硬币凑成总金额 j 的硬币组合数
int[][] dp = new int[coins.length + 1][amount + 1];

dp[0][0] = 1; // 前 0 个硬币只能凑成总金额 0, 即只有一种方案

// 总金额 0 可以由前 i 个硬币中选择 0 个硬币构成,即只有一种方案
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 i = 1; i <= coins.length; i++) {
for (int j = coins[i - 1]; j <= amount; j++) {
dp[j] += dp[j - coins[i - 1]];
}
}

return dp[amount];
}
}
Reference

518. Coin Change 2
『 一文搞懂完全背包 』从0-1背包到完全背包,逐层深入+数学推导