硬币找零问题

假如现在有四种硬币类型: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];
    }


}

 

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

posted @ 2019-06-02 18:16  猫咪大王_lkb  阅读(488)  评论(0编辑  收藏  举报