• 硬币找零问题


    假如现在有四种硬币类型:1角,2角,5角和1元。

    你现在是超市收银员,老板要求你每次都使用最少的硬币给用户找零。

    例如,用户需要找零6角,你需要找给他 一个5角 + 一个1角,这样只用到2个硬币,而不是找给他 六个1角 或者 三个2角。

    面对这样的问题我们该如何思考呢?

    假如当前已经选择了 i-1 枚硬币,当选择下一枚硬币 i 的时候,面对下面两种情况:

    面对这两种选择我们需要做的就是选择其中的最优解。

    下面以找零6角为例进行一下推算。

    找零1角,可供选择的硬币为1角,那此时的最优解在选择这1角硬币和不选择这1角硬币中。

    情况一:如果选择这1角硬币,这意味着需要的硬币数量 = ((找零1角 - 当前硬币金额 )所需要的硬币数量 )+ 1

    找零1角 - 当前硬币金额 = 1 -1 = 0,而找零0角需要的硬币数量为0,所以第一种情况需要的硬币数量 = 0 + 1 = 1

    情况二:如果不选择这枚硬币,这意味着需要的硬币数量 = 之前 1 - 1 = 0 种硬币找零1角的最有解。

    我们知道如果硬币种类为0,我们是无法进行找零的,也就是需要找零的硬币为无穷大。

    显然情况一需要的硬币数量更少。此时的最优解为1。

    同理我们可以推论出后面的结果。下图显示了这个推算过程。

    如上推算所示,我们在推演时需要考虑一些边界条件:

    1、如果当前没有硬币,是无法找零的,也就是无论多少硬币都无法找零。

    2、如果需要找零的金额为0,那这时候也需要找零。

    3、如果需要找零的金额数量小于硬币的面额,那这枚硬币就不需要使用。比如找零金额为6角,那1元的硬币肯定是用不着的。

    1、2 两个边界条件在找零钱前应该是默认成立的,所以在编写程序的时候需要将它们设置为初始条件。

    边界条件3则是在我们找零过程中需要进行判断的,在编程时候应该写在找零程序中。

    好了,说了这么多,还是直接上代码方便快捷= =

    package com.lkb.dp.problems;
    
    import java.util.Arrays;
    
    /**
     * @Description 硬币找零问题
     * 假设只有 1角,2角,5角,1元的硬币,
     * 在超市结账时,如果需要找零钱,收银员希望将最少的硬币数找给顾客。那么,给定需要找的零钱数目,如何求得最少的硬币数呢?
     * @Author lkb
     * @CreateDate: 2019/6/1
     */
    public class ChargeProblem {
    
        public static void main(String[] args) {
            int[] coinsValues = {1,2,5,10};
            Arrays.sort(coinsValues);
            int n = 6;
            int minCoinsNumber = charge(coinsValues, n);
            System.out.println(minCoinsNumber);
        }
    
    
        /**
         * 功能描述: 硬币找零问题
         * @author lkb
         * @date 2019/6/1
         * @param
         * @return int
         */
        public static int charge(int[] coinKind, int money){
            //这个数组保存最优解
            //例如  value[2][5] 表示 使用 coinKind[0] coinKind[1] coinKind[2] 找零 money = 5 的最优解
            int[][] value = new int[coinKind.length+1][money+1];
    
            //边界条件1:如果硬币类型为0,则永远找不到合适的硬币找零 、
            // 这里设置为最大值是因为我们后续比较使用
            for(int i=0; i<=money; i++){
                value[0][i] = Integer.MAX_VALUE;
            }
            //边界条件2:如果钱为0,则永远找不到合适的硬币找零
            for(int i=0; i<=coinKind.length; i++){
                value[i][0] = 0;
            }
    
            for(int mon=1; mon<=money; mon++){
                for(int kind=1; kind<=coinKind.length; kind++){
                    //边界条件2:如果硬币金额大于钱,则最优解是 上一步的最优解
                    if(coinKind[kind-1] > mon){
                        value[kind][mon] = value[kind-1][mon];
                        continue;
                    }
    
                    //当前最优解分为两种情况,需要在下面两种情况选择一种解最小的情况
                    //一个是使用了当前类型的硬币,value[kind][mon - coinKind[kind-1]] + 1
                    // 一个是没有使用当前类型的硬币, value[kind-1][mon]
                    if((value[kind][mon - coinKind[kind-1]] + 1) < value[kind-1][mon]){
                        value[kind][mon] = value[kind][mon - coinKind[kind-1]] + 1;
                    }else{
                        value[kind][mon] = value[kind-1][mon];
                    }
                }
            }
    
            return value[coinKind.length][money];
        }
    
    
    }

    大家也可以在这个地址下载源码,或者关注我的公众号,一起见证我的成长。

  • 相关阅读:
    块设备驱动框架分析(一)
    LIN总线协议
    LCD驱动分析(三)时序分析
    str_shuffle — 随机打乱一个字符串
    str_replace — 子字符串替换
    str_repeat — 重复一个字符串
    str_pad — 使用另一个字符串填充字符串为指定长度
    str_getcsv — 解析 CSV 字符串为一个数组
    ltrim — 删除字符串开头的空白字符(或其他字符)
    lcfirst — 使一个字符串的第一个字符小写
  • 原文地址:https://www.cnblogs.com/catlkb/p/10960505.html
Copyright © 2020-2023  润新知