问题描述
* 假设我们有8种不同面值的硬币{1,2,5,10,20,50,100,200},用这些硬币组合构成一个给定的数值n。
* 例如n=200,那么一种可能的组合方式为 200 = 3 * 1 + 1*2 + 1*5 + 2*20 + 1 * 50 + 1 * 100.
* 问总共有多少种可能的组合方式? (这道题目来自著名编程网站ProjectEuler) 类似的题目还有:
[华为面试题] 1分2分5分的硬币三种,组合成1角,共有多少种组合
1*x + 2*y + 5*z=10
[创新工厂笔试题] 有1分,2分,5分,10分四种硬币,每种硬币数量无限,给定n分钱,有多少组合可以组成n分钱
1 5 10 25 分 n,多少种组合方法.
- 1 递归
// n 多少钱 cols[1,5,10,25] cur当前最大面值下标
private int rescursion(int n, int[] cols, int cur) {
if (cur<=0) return 1;
int res =0;
/* 剩余
100最大面值 0*25 +100
1*25 +75
..
4*25 +0
*/
for (int i=0;i<=n/cols[cur];i++){
int surplus =n -i*cols[cur];
res+= rescursion(surplus,cols,cur-1);
}
return res;
}
- dp 动态规划
f[i][j] 便是前i种币 表示面值j的方法数
//递推 dp
private int dp(int money, int[] cols) {
int f[][]=new int[cols.length][money+1];
for (int i=0;i<4;i++){
f[i][0]=1;
}
for (int i=0;i<=money;i++){
f[0][i]=1;
}
for (int i=1;i<cols.length;i++){
for (int j=1;j<=money;j++){
if(j>=cols[i]){
f[i][j] =f[i-1][j]+f[i][j-cols[i]];
}else {
f[i][j]=f[i-1][j];
}
}
}
/*打印二维矩阵
for (int i=0;i<cols.length;i++){
for (int j=0;j<=money;j++){
System.out.print(" "+f[i][j]);
}
System.out.println();
}
*/
return f[cols.length-1][money];
}
由于f[i][j] 只与 他的 左边和上一行数据有关
f[j]可以是一个循环过程的上一次记录 所以可以降维处理
public int dp2(int n) {
int[] coins = {1, 5, 10, 25};
int[] dp = new int[n + 1];
dp[0] = 1;
for (int i = 0; i < 4; i++) {
for (int j = coins[i]; j < n + 1; j++) {
dp[j] = (dp[j] + dp[j - coins[i]]) % 1000000007;
}
// System.out.println(Arrays.toString(dp));
}
return dp[n];
}