- 问题描述
1分2分5分的硬币,组成1角,共有多少种组合。
不妨将这个问题一般化,即给定一个非负数组,求有多少种组合可以组成n。
- 解决方案(动态规划)
- dp[i][n]表示用前i + 1中硬币所能组成的n的种数
- 递推关系:dp[i][sum] = dp[i-1][sum - 0Vm] + dp[i-1][sum - 1Vm] + dp[i-1][sum - 2Vm] + … + dp[i-1][sum - KVm]; 其中K = sum / Vm。
- 返回值。返回dp[i][n]。
代码如下:
/**
* n为总数,coins数组为硬币的面值,求有多少种组合
* @param n
* @param coins
* @return
*/
public static int coinGroups(int n,int[] coins){
//dp[i][n]表示用前i + 1中硬币所能组成的n的种数
int[][] dp = new int[coins.length][n + 1];
for(int i = 0;i <= n;i ++){
if(i % coins[0] == 0)
dp[0][i] = 1;
}
for(int i = 1;i < coins.length;i ++){
for(int k = 0;k <= n;k ++){
for(int j = 0;j <= k / coins[i];j ++){
dp[i][k] = dp[i - 1][k - j * coins[i]] + dp[i][k];
}
}
}
return dp[coins.length - 1][n];
}
- 优化
方案1的空间复杂度较大,可以优化,代码如下:
public static int coinGroups2(int n,int[] coins) {
int[] dp = new int[n + 1];
dp[0] = 1;
for(int i = 0;i < coins.length;++i){
for(int j = coins[i];j <= n;++j){
dp[j] =dp[j]+dp[j-coins[i]];
}
}
return dp[n];
}