程序员的算法趣题:Q05 还在用现金支付吗(Java版)

题目说明

纸币兑换硬币
硬币种类有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

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值