考虑仅用1分、5分、10分、25分和50分这5种硬币支付某一个给定的金额。例如需要支付11分钱,有一个1分和一个10分、一个1分和一个5分、六个1分和一个5分、十一个1分这4种方式。请写一个程序,计算一个给定的金额有几种支付方式。注:假定支付0元有1种方式。
输入包含多组数据。每组数据包含一个正整数n(1≤n≤10000),即需要支付的金额。
对应每一组数据,输出一个正整数,表示替换方式的种数。
11
26
4
13
假设硬币的种类数组t={1,5,10,25,50},按大小排序。m表示选择有0~m种硬币可以选择,面值是t[0]、•••、t[m-1]。要换的钱的数目是n。本题可以使用动态规划算法解决。
假设有n钱待找零,当前可以供选择的方式为m种,f(n,m)表示共的待找零方案,则有递推公式:
当n=0,f(n,m)=1,表示已经找零完毕,再找0元只有一种方案。
当n<0表示这种方案找零不合理,不能完成找零操作,而m≤0说明找零还没有完成,但是已经没有可以供选择的硬币了。所以f(n,m)=0。
对于可以找零,并且还有硬币选择的情况找零有两种方案。第一种是:选择一个可以选择的最大面值的,剩下的钱再进行找零操作,同时硬币种类的选择方案没有变化,即为:f(n-t[m-1],m)。第二种是:现在和以后都不选择本次可以选择的最大的硬币面值,然后再进行找零操作。即f(n,m-1)。
假设有n钱待找零,当前可以供选择的方式为m种,创建一个长度为n+1的数组r,r[i]表示找零为i的找零方法为r[i]。初始时r的第一个元素为1,其它元素都为0,即r[0]=1,r[i≠0]=0。
步骤一、因为硬币的面值都按大小排序,从最小的面值开始选择,先选择最小的一个t[0]。对于找零为大于0的情况只有从t[0]开始才可能有找零的情况。对于i≥t[0]有r[i]=r[i]+r[i-t[0]]。这是只有一种硬币可以选择的情况。
步骤二、当有2种硬币可以选择,在步骤一已经求出了只有一种硬币可供选择的情况,现在可以选择第二种硬币,那么只有找零数i≥t[0]时才可以选择第二种硬币,所以有r[i]=r[i]+r[i-t[1]]。
同理可以求得有3、4、•••。具体实现详见代码。
import java.util.Scanner;
/**
* Author: 王俊超
* Time: 2016-05-13 15:15
* CSDN: http://blog.csdn.net/derrantcm
* Github: https://github.com/Wang-Jun-Chao
* Declaration: All Rights Reserved !!!
*/
public class Main {
// 硬币可以供选择的面值
private final static int[] T = {1, 5, 10, 25, 50};
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// Scanner scanner = new Scanner(Main.class.getClassLoader().getResourceAsStream("data.txt"));
while (scanner.hasNext()) {
int n = scanner.nextInt();
// System.out.println(exchange(n, T.length));
System.out.println(exchange(n));
}
scanner.close();
}
/**
* 解法一:递归解法
* 找零操作
*
* @param n 当前要找的零钱数
* @param m 可以选择的硬币种类T[0]~T[m-1]
* @return 不的找零数目
*/
private static int exchange(int n, int m) {
if (n == 0) {
return 1;
} else if (n < 0 || m <= 0) {
return 0;
} else {
return exchange(n - T[m - 1], m) + exchange(n, m - 1);
}
}
/**
* 解法二:非递归解法
* 找零操作
*
* @param n 当前要找的零钱数
* @return 不的找零数目
*/
private static long exchange(int n) {
// 选择long不然可能会超出int表达范围
long[] result = new long[n + 1];
// 初始化
result[0] = 1;
// 每次增加一种硬币,且比之前硬币的面值大,计算加入新的硬币后每个数目的钱其找零数有多少种
for (int t : T) {
for (int j = t; j <= n; j++) {
result[j] += result[j - t];
}
}
return result[n];
}
}
因为markddow不好编辑,因此将文档的图片上传以供阅读。Pdf和Word文档可以在Github上进行【下载>>>】。