题目
假设我们有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. 问总过有多少种可能的组合方式?原题 转载
分析
这道题目是非常经典的动态规划算法题。给定一个数值sum,假设我们有m种不同类型的硬币
求所有可能的组合数,就是求满足前面等值的系数
思路1:
用暴力枚举,各个系数可能的取值无非是
,这对于硬币种类数较小还可以应付。
思路2:
从上面的分析中我们也可以这么考虑,我们希望用m种硬币构成sum,根据最后一个硬币
…
其中
定义dp[i][sum] = 用前i种硬币构成sum 的所有组合数。
那么题目的问题实际上就是求dp[m][sum],即用前m种硬币(所有硬币)构成sum的所有组合数。
在上面的联合等式中,当
其中
初始情况:如果sum=0,那么无论有前多少种来组合0,只有一种可能,就是各个系数都等于0,
如果我们用二位数组表示dp[i][sum], 我们发现第i行的值全部依赖与i-1行的值,所以我们可以逐行求解该数组。如果前0种硬币要组成sum,我们规定为dp[0][sum] = 0。
思路3:硬币组合问题,本质上就是组合数的问题,解决组合问题,非常经典的算法是回溯算法,它在无限的解空间中深度优先搜索。
第一种动态规划算法实现如下:
import java.util.Scanner;
/**
* 有几种纸币面值1, 5, 10, 20, 50, 100元,假设每种面值的纸币无限,用它们组合成N元。找出所有的组合数。
* @author ShaoCheng
* @version 1.0 2015-9-19
*/
public class Solution {
/**
* @param N 输入的总和N
* @return 所有的组合数
*/
public long getNumberOfCombinations(int N) {
int coinKinds = coins.length;
int[][] dp = new int[coinKinds+1][N+1];
for(int i = 0; i <= coinKinds; i++){ //初始化
for(int j = 0; j <= N; ++j){
dp[i][j] = 0;
}
}
for(int i = 0; i <= coinKinds; i++){
dp[i][0] = 1;//前i种纸币组合成0,只有一种情况就是个数均为0
}
for(int i = 1; i <= coinKinds; i++){
for(int j = 1; j <= N; j++){
dp[i][j] = 0;
for(int k = 0; k <= j / coins[i-1]; k++){
dp[i][j] += dp[i-1][j - k*coins[i-1]];
}
}
}
return dp[coinKinds][N];
}
public static void main(String[] args){
Solution sl = new Solution();
Scanner scanner = new Scanner(System.in);
int N = scanner.nextInt();
long res = sl.getNumberOfCombinations(N);
System.out.println(res);
scanner.close();
}
public Solution() {
// TODO Auto-generated constructor stub
coins = new int[]{1, 5, 10, 20, 50, 100};
}
private int[] coins;
}
回溯算法实现如下:
import java.util.Arrays;
import java.util.Scanner;
/**
* 有几种纸币面值1, 5, 10, 20, 50, 100元,假设每种面值的纸币无限,用它们组合成N元。找出所有的组合数。
* @author ShaoCheng
* @version 1.1 2015-9-20
*/
public class Solution {
/**
* @param N 输入的总和N
* @return 所有的组合数
*/
public long getNumberOfCombinations(int N) {
long sum = 0;
return getNumberOfCombinations(N, sum, 0);
}
public long getNumberOfCombinations(int N, long sum, int start){
long count = 0;
for(int i = start; i < coins.length; i++){
sum += coins[i];
if(sum == N){
count++;
break;
}
if(sum < N){
count += getNumberOfCombinations(N, sum, i);
sum -= coins[i];
}
else
break;
}
return count;
}
public static void main(String[] args){
Solution sl = new Solution();
Scanner scanner = new Scanner(System.in);
int N = scanner.nextInt();
long res = sl.getNumberOfCombinations(N);
System.out.println(res);
scanner.close();
}
public Solution() {
// TODO Auto-generated constructor stub
coins = new int[]{1, 5, 10, 20, 100, 50}; //如果乱序,应先排序
Arrays.sort(coins);
}
private int[] coins;
}