题目:给你六种面额 1、5、10、20、50、100 元的纸币,假设每种币值的数量都足够多,编写程序求组成N元(N为0~10000的非负整数)的不同组合的个数。
时间空间限制:
时间限制:1秒
空间限制:32768K
输入描述:
输入包括一个整数n(1 ≤ n ≤ 10000)
输出描述:
输出一个整数,表示不同的组合方案数
输入例子1:
1
输出例子1:
1
解题思路:
这道题是一道经典的动态规划题目。题目意思是给定一个输入N,假设我们有m中不同面值的钱币v1,v2,v3...,Vm,则用这m中不同的钱币组合成N,即N=t1*v1+t2*v2+t3*v3+...+tm*Vm (t1...tm表示所需不同面值钱币对应的次数),求t1,t2...tm的组合数。
即求有多少种t1,t2...tm满足N=t1*v1+t2*v2+t3*v3+...+tm*Vm
动态规划思想:
定义dp[i][total] 表示前i种钱币组合成total的组合次数。假设我们前面所有钱币已经组合成功,则最后一种钱币的所有可能取法为tm=0,1,2...k,其中k=total/Vm;
当tm=0, dp[i][total]=dp[i-1][total]
当tm=1,dp[i][total]=dp[i-1][total-1*Vm]
....
当tm=k,dp[i][total]=dp[i-1][total-k*Vm]
所有组合数就是dp[i][total]=dp[i-1][total]+dp[i-1][total-1*Vm]+....+dp[i-1][total-k*Vm]
初始化用一个二维数组dp[i][total]来表示前i中钱币拼凑成total的组合数。开始初始化数组dp[i][total]全部为0;
然后初始化total=0的时候,dp[i][0]=1,表示总的钱币为0的时候,组合情况只有一种即取钱币0。
由于最后一种钱币的取法依赖于前一种钱币的取法,所以先求出前i-1中的钱币组合,再求最后一种。以此类推,从头依次向后遍历即可,最后dp[i][total] 为题目所求。
代码如下:
import java.util.Scanner;
public class CoinFen {
public static void main(String[] args) {
int [] coins= {1,5,10,20,50,100}; //所有钱币的种类
Scanner in=new Scanner(System.in);
int n=in.nextInt(); //待拼凑钱币总数
System.out.println(getCombinetimes(n,coins));
}
//求解所有组合数,由于组合数超过Integer.MAXVALUE故使用long类型
public static long getCombinetimes(int N,int[]coins) {
int coinsVariety=coins.length; //钱币的种类数
long [][] dp=new long[coinsVariety+1][N+1];
//初始化
for(int i=0;i<=coinsVariety;i++) {
for(int j=0;j<=N;j++) {
dp[i][j]=0; //初始化
}
}
//N=0的时候
for(int i=0;i<=coinsVariety;i++) { //拼凑的钱币为0的时候只有一种组合数即选0个钱币这一种可能。
dp[i][0]=1;
}
for(int i=1;i<=coinsVariety;i++) { //遍历钱币所有种类
for(int j=1;j<=N;j++) { //拼凑钱币数
dp[i][j]=0;
for(int k=0;k<=j/coins[i-1];k++) { //对于每一中钱币,相对于平凑钱币数j所有可能取法
dp[i][j]+=dp[i-1][j-k*coins[i-1]];
}
}
}
return dp[coinsVariety][N];
}
}