题目描述
- 给定两个数组: n1 n2
- n1[i]: 表示面值,n1币种为普通币,可以取任意枚
- n2[i]: 表示面值,n1币种为纪念币,只能取一枚
- 问能用多少种方法拼出aim的面值
code
public class Code35 {
/**
* @param commonCoin 普通硬币
* @param goldCoin 纪念币
* @param money 钱数
*/
public static int money(int[] commonCoin, int[] goldCoin, int money) {
if (money < 0) {
return 0
}
if ((commonCoin == null || commonCoin.length == 0) && (goldCoin == null || goldCoin.length == 0)) {
return money == 0 ? 1 : 0
}
// 任意张 的数组, 一张的数组,不可能都没有
int[][] dparb = commonCoin(commonCoin, money)
int[][] dpone = goldCoin(goldCoin, money)
if (dparb == null) { // 任意张的数组没有,一张的数组有
return dpone[dpone.length - 1][money]
}
if (dpone == null) { // 任意张的数组有,一张的数组没有
return dparb[dparb.length - 1][money]
}
int res = 0
for (int i = 0
res += dparb[dparb.length - 1][i] * dpone[dpone.length - 1][money - i]
}
return res
}
/**
* 只用纪念币(普通背包问题)
* <p>
* dp[i][j] 0...i号货币自由选择,搞定j钱数的方法数 (每个货币只可以使用一次)
* 第一列:搞定0元,一张也不要 全是1
* 第一行:j == arr[i]时为1 否则为0 (这里只能用一次)
* 普通位置:
* 不使用i号货币 dp[i - 1][j]
* 使用i号货币 dp[i-1][j-arr[i]]
*/
public static int[][] goldCoin(int[] arr, int money) {
if (arr == null || arr.length == 0) {
return null
}
int[][] dp = new int[arr.length][money + 1]
for (int i = 0
dp[i][0] = 1
}
if (arr[0] <= money) {
dp[0][arr[0]] = 1
}
for (int i = 1
for (int j = 1
dp[i][j] = dp[i - 1][j]
dp[i][j] += j - arr[i] >= 0 ? dp[i - 1][j - arr[i]] : 0
}
}
return dp
}
/**
* 只用普通货币
* dp[i][j] 0...i号货币自由选择,搞定j钱数的方法数(每个货币可以使用无限次)
* <p>
* 第一列:搞定0元,一张也不要 全是1
* 第一行:j == arr[i]的倍数 时为1 否则为0
* <p>
* dp[i][j] = dp[i-1][j] + dp[i][j-arr[i]] (j-arr[i] 不越界)
*/
public static int[][] commonCoin(int[] arr, int money) {
if (arr == null || arr.length == 0) {
return null
}
int[][] dp = new int[arr.length][money + 1]
// dp[i][j] 0..i券 自由选择张数, 搞定j元, 有多少方法?
for (int i = 0
dp[i][0] = 1
}
// [0] 5元 0元 5元 10元 15元 20元
for (int j = 1
dp[0][arr[0] * j] = 1
}
// 0行 0列 填完了
for (int i = 1
for (int j = 1
dp[i][j] = dp[i - 1][j]
dp[i][j] += j - arr[i] >= 0 ? dp[i][j - arr[i]] : 0
}
}
return dp
}
}