问题描述:硬币分别有面值分别为1,5, 10, 25四种面值。输入一个整数n,用这些硬币组合成数值n,问有多少种组合方式?
解法一:暴力破解,用四个for循环破解。
代码如下:
import java.util.Scanner;
public class 硬币凑数问题 {
public static void main(String[] args){
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
System.out.println(solve(n));
}
/**
* for循环暴力法破解
* @param n
* @return
*/
private static int solve(int n) {
int sum = 0;
for(int i=0; i<=n; i++){
for(int j=0; j*5<=n; j++){
for(int k=0; k*10<=n; k++){
for(int l=0; l*25<=n; l++){
if((i+5*j+k*10+l*25 == n)) sum++;
}
}
}
}
return sum;
}
}
解法二:用递推的方法实现,这个递归关键在于变量的设置,设三个变量,第一个是需要凑的数值n;第二个是一个数组表arr示硬币的面值,是固定不变的;第三个是硬币的可用范围m,也就是数组下标。当前可用的最大硬币面值是arr[m]。
分解过程是这样的将凑整数n分解为:用一个arr[m]参与组合,用2arr[m]参与组合,用3arr[m]参与组合,一直分解到iarr[m], 当ia>n时停止。
边界出口就是当m=0时,也就是只能用面值为1的硬币进行组合的时候,这时只有一种组合(剩余的数值都用面值为1的硬币来凑)。当n=0时,返回1,而不是返回0;返回0的话最终结果会比正确答案少1。因为n=0表示的情况是剩余的数值是0,也就是说上一次数值刚好是最大面值的整数倍。举个列子:假设数值是100,则刚好可以用4个25面值的硬币表示剩余值为0;这也一种组合方式。所以应该返回的值1。
这题需要注意的地方是它是多分支的分解情况,与寻常递归只有两三种分支情况不同(比如斐波那契数列求和)。所以这题需要再循环中进行递归。
递归最需要注意的地方就是分解的表达式和出口是否正确。
代码如下:
import java.util.Scanner;
public class 硬币凑数问题 {
public static void main(String[] args){
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
int[] a = {1,5,10,25};
System.out.println(solve(n, a, 3));
}
private static int solve(int n, int[] a, int m) {
int res = 0;
if(m==0) return 1;
if(n==0) return 1;
for(int i=0; i*a[m]<=n; i++){
res = res + solve(n-i*a[m], a, m-1);
}
return res;
}
}