动态规划实例(九):最小硬币找零数

本文介绍了一种使用动态规划解决硬币找零问题的方法,旨在找出组成特定金额所需的最少硬币数量。通过逐个计算每个金额的最优解,并利用之前计算的结果来减少重复计算,从而实现了高效求解。
    硬币找零问题描述:现存在一堆面值为 V1、V2、V3 … 个单位的硬币,问最少需要多少个硬币才能找出总值为 T 个单位的零钱?
    假设这一堆面值分别为 1、2、5、21、25 元,需要找出总值 T 为 63 元的零钱。很明显,只要拿出 3 个 21 元的硬币就凑够了 63 元了。
    基于上述动态规划的思想,我们可以从 1 元开始计算出最少需要几个硬币,然后再求 2 元、3元…每一次求得的结果都保存在一个数组中,以后需要用到时则直接取出即可。那么我们什么时候需要这些子问题的解呢?如何体现出由子问题的解得到较大问题的解呢?
    其实,在我们从 1 元开始依次找零时,可以尝试一下当前要找零的面值(这里指 1 元)是否能够被分解成另一个已求解的面值的找零需要的硬币个数再加上这一堆硬币中的某个面值之和,如果这样分解之后最终的硬币数是最少的,那么问题就得到答案了。
    单是上面的文字描述太抽象,先假定以下变量:
         values[] : 保存每一种硬币的币值的数组
         valueKinds :币值不同的硬币种类数量,即values[]数组的大小
         money : 需要找零的面值
         coinsUsed[] : 保存面值为 i 的纸币找零所需的最小硬币数
    算法描述:

    当求解总面值为 i 的找零最少硬币数 coinsUsed[ i ] 时,将其分解成求解 coinsUsed[ i – cents]和一个面值为 cents 元的硬币,由于 i – cents < i , 其解 coinsUsed[ i – cents] 已经存在,如果面值为 cents 的硬币满足题意,那么最终解 coinsUsed[ i ] 则等于 coinsUsed[ i – cents] 再加上 1(即面值为 cents)的这一个硬币。

具体实例及代码如下所示:

/**
 * @Title: MinCoinsChangeCount.java
 * @Package dynamicprogramming
 * @Description: TODO
 * @author peidong
 * @date 2017-6-7 上午9:18:37 
 * @version V1.0
 */
package dynamicprogramming;

/**
 * @ClassName: MinCoinsChangeCount
 * @Description: 最小硬币找零数
 * @date 2017-6-7 上午9:18:37 
 *
 */

public class MinCoinsChangeCount {
    /**
     *
     * @Title: makeChange
     * @Description: 硬币找零
     * @param values  保存每一种硬币的币值数组
     * @param valueKinds 币值不同的硬币种类数量,values数组的大小
     * @param money 需要找零的面值
     * @param coinsUsed 保存面值为i的纸币找零所需的最小硬币数
     * @return void
     * @throws
     */
    public static void makeChange(int[] values, int valueKinds, int money, int[] coinsUsed) {

        coinsUsed[0] = 0;
        // 对每一分钱都找零,即保存子问题的解以备用,即填表  
        for (int cents = 1; cents <= money; cents++) {

            // 当用最小币值的硬币找零时,所需硬币数量最多  
            int minCoins = cents;

            // 遍历每一种面值的硬币,看是否可作为找零的其中之一  
            for (int kind = 0; kind < valueKinds; kind++) {
                // 若当前面值的硬币小于当前的cents则分解问题并查表  
                if (values[kind] <= cents) {
                    int temp = coinsUsed[cents - values[kind]] + 1;
                    if (temp < minCoins) {
                        minCoins = temp;
                    }
                }
            }
            // 保存最小硬币数  
            coinsUsed[cents] = minCoins;

            System.out.println("面值为 " + (cents) + " 的最小硬币数 : "
                    + coinsUsed[cents]);
        }
    }

    /**
     * @Title: main
     * @Description: TODO
     * @param args
     * @return void
     * @throws
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        // 硬币面值预先已经按降序排列  
        int[] coinValue = new int[] { 25, 21, 10, 5, 1 };
        // 需要找零的面值  
        int money = 63;
        // 保存每一个面值找零所需的最小硬币数,0号单元舍弃不用,所以要多加1  
        int[] coinsUsed = new int[money + 1];

        makeChange(coinValue, coinValue.length, money, coinsUsed);

    }

}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值