题目说明
纸币兑换硬币
硬币种类有10、50、100、500日元
一次兑换出的硬币数量不超过15个
求:1000日元纸币兑换出的硬币有多少种组合(不考虑硬币兑换出来的先后顺序)
思路1
1.一层循环罗列一种硬币的数量
2.多重循环嵌套罗列出所有合理的可能性
3.通过硬币数量<=15和硬币总金额==1000筛选出正确结果
代码1
public static void main(String[] args) {
System.out.println("10\t50\t100\t500");
int count = 0; // 统计有多少种组合
for(int a = 0; a <= 15; a ++){ // 10日元的硬币数量
for(int b = 0; b <= 15; b ++){ // 50日元的硬币数量
for(int c = 0; c <= 10; c ++){ // 100日元的硬币数量
for(int d = 0; d <= 2; d ++){ // 500日元的硬币数量
// 硬币总数<=15; 金额总和为1000日元
if(a+b+c+d <= 15 && 10*a + 50*b + 100*c + 500*d == 1000){
System.out.println(a+"\t"+b+"\t"+c+"\t"+d);
count ++;
}
}
}
}
}
System.out.println("count = " + count);
}
结果1
10 50 100 500
0 0 0 2
0 0 5 1
0 0 10 0
0 2 4 1
0 2 9 0
0 4 3 1
0 4 8 0
0 6 2 1
0 6 7 0
0 8 1 1
0 8 6 0
0 10 0 1
0 10 5 0
5 1 4 1
5 1 9 0
5 3 3 1
5 5 2 1
5 7 1 1
5 9 0 1
10 0 4 1
count = 20
思路2
将循环改为递归
代码2
private static int count = 0; // 统计有多少种结果
public static void main(String[] args) {
List<Integer> coins = new LinkedList<>();
coins.addAll(Arrays.asList(10,50,100,500));
change(1000, 15, coins, new LinkedList<>()); // 自定义的方法change(xx)
System.out.println(count);
}
/**
* 每一层递归,使用一种硬币
* @param target 剩余目标金额
* @param usable 剩余可用硬币数量
* @param coins 剩余可用硬币种类:10,50,100,500
* @param log 记录每一次递归使用了哪种硬币,用了几个(仅用于详细展示每种结果,可删)
* @return
*/
public static void change(int target, int usable, List<Integer> coins, List<String> log){
if(target < 0) return ; // 硬币总金额超出1000日元
if(usable < 0) return ; // 硬币总数量超出15个硬币
if(target == 0) {
log.forEach(System.out::println); // Lambda表达式遍历集合(JDK8+可用)
System.out.println("-------------------------------------------------------------------------");
count ++; // 正确答案+1
return ;
}
if(coins.size() == 0) return ; // 没有可用硬币种类
int coin = coins.remove(0); // 本轮取出的硬币种类:比如10日元
for(int i = 0; i <= usable; i ++){ // 变量i表示本轮兑换的面额为coin的硬币数量(依次尝试0个,1个,...,usable个)
log.add(String.format("剩余金额%1$-8s使用"+coin+"日元\t"+i+"个,\t还可用"+(usable-i)+"个硬币,\t剩余种类"+coins,(target-coin*i)+","));
change(target - coin * i, usable-i, coins, log);
log.remove(log.size()-1); // 一种情况尝试完毕,要恢复log集合在本次尝试之前的内容状态
}
coins.add(0, coin); // 一种情况尝试完毕,要恢复coins集合在本轮尝试之前的内容状态
}
结果2
剩余金额1000, 使用10日元 0个, 还可用15个硬币, 剩余种类[50, 100, 500]
剩余金额1000, 使用50日元 0个, 还可用15个硬币, 剩余种类[100, 500]
剩余金额1000, 使用100日元 0个, 还可用15个硬币, 剩余种类[500]
剩余金额0, 使用500日元 2个, 还可用13个硬币, 剩余种类[]
-------------------------------------------------------------------------
剩余金额1000, 使用10日元 0个, 还可用15个硬币, 剩余种类[50, 100, 500]
剩余金额1000, 使用50日元 0个, 还可用15个硬币, 剩余种类[100, 500]
剩余金额500, 使用100日元 5个, 还可用10个硬币, 剩余种类[500]
剩余金额0, 使用500日元 1个, 还可用9个硬币, 剩余种类[]
-------------------------------------------------------------------------
......省略部分内容......
-------------------------------------------------------------------------
剩余金额950, 使用10日元 5个, 还可用10个硬币, 剩余种类[50, 100, 500]
剩余金额500, 使用50日元 9个, 还可用1个硬币, 剩余种类[100, 500]
剩余金额500, 使用100日元 0个, 还可用1个硬币, 剩余种类[500]
剩余金额0, 使用500日元 1个, 还可用0个硬币, 剩余种类[]
-------------------------------------------------------------------------
剩余金额900, 使用10日元 10个, 还可用5个硬币, 剩余种类[50, 100, 500]
剩余金额900, 使用50日元 0个, 还可用5个硬币, 剩余种类[100, 500]
剩余金额500, 使用100日元 4个, 还可用1个硬币, 剩余种类[500]
剩余金额0, 使用500日元 1个, 还可用0个硬币, 剩余种类[]
-------------------------------------------------------------------------
20