动态规划求解硬币找零问题

动态规划的基本思想是将待求解问题分解成若干个子问题,先求解子问题,并将这些子问题的解保存起来,如果以后在求解较大子问题的时候需要用到这些子问题的解,就可以直接取出这些已经计算过的解而免去重复运算。保存子问题的解可以使用填表方式,例如保存在数组中。

下面用一个实际例子来体现动态规划的算法思想——硬币找零问题。

T个单位的零钱?假设这一堆面值分别为 1、2、5、21、25 元,需要找出总值 T 为 63 元的零钱。

很明显,只要拿出 3 个 21 元的硬币就凑够了 63 元了。

基于上述动态规划的思想,我们可以从 1 元开始计算出最少需要几个硬币,然后再求 2 元、3元…每一次求得的结果都保存在一个数组中,以后需要用到时则直接取出即可。那么我们什么时候需要这些子问题的解呢?如何体现出由子问题的解得到较大问题的解呢?

其实,在我们从 1 元开始依次找零时,可以尝试一下当前要找零的面值(这里指 1元)是否能够被分解成另一个已求解的面值的找零需要的硬币个数再加上这一堆硬币中的某个面值之和,如果这样分解之后最终的硬币数是最少的,那么问题就得到答案了。

单是上面的文字描述太抽象,先假定以下变量:

values[] : 保存每一种硬币的币值的数组

valueKinds :币值不同的硬币种类数量,即values[]数组的大小

money : 需要找零的面值

算法描述:

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

算法实现:

package com.dynamic;/** * @author sjmei * @date 2012-10-30 */public class CoinsChange {/** * * 硬币找零:动态规划算法 * @param values:保存每一种硬币的币值的数组 * * @param valueKinds:币值不同的硬币种类数量,即coinValue[]数组的大小 * * @param money:需要找零的面值 * * @param coinsUsed:保存面值为i的纸币找零所需的最小硬币数 */public static void makeChange(int[] values, int valueKinds, int money,int[] coinsUsed,int[] coinTrack) {coinsUsed[0] = 0;int last = 0;// 对每一分钱都找零,即保存子问题的解以备用,即填表for (int cents = 1; cents <= money; cents++) {// 当用最小币值的硬币找零时,所需硬币数量最多int minCoins = 999;// 遍历每一种面值的硬币,看是否可作为找零的其中之一for (int kind = 0; kind < valueKinds; kind++) {// 若当前面值的硬币小于当前的cents则分解问题并查表if (values[kind] <= cents) {int temp = coinsUsed[cents – values[kind]] + 1;if (temp < minCoins) {minCoins = temp;last = kind;}}}// 保存最小硬币数coinsUsed[cents] = minCoins;coinTrack[cents] = values[last];System.out.print("面值为 :" + (cents) + "的最小硬币数 : "+coinsUsed[cents]);System.out.print(" 硬币为:");trackPrint(cents, coinTrack);System.out.println();}}private static void trackPrint(int m,int[] coinTrack){if(m==0){return;}else {System.out.print(coinTrack[m]+" ");trackPrint(m-coinTrack[m], coinTrack);}}public static void main(String[] args) {// 硬币面值预先已经按降序排列int[] coinValue = new int[] { 25, 21, 10, 5, 1 };// 需要找零的面值int money = 65;// 保存每一个面值找零所需的最小硬币数,0号单元舍弃不用,所以要多加1int[] coinsUsed = new int[money+1];int[] coinTrack = new int[money+1];for(int i=1;i<=money;i++){coinsUsed[i] = 0;coinTrack[i] = 0;}makeChange(coinValue, coinValue.length, money, coinsUsed,coinTrack);}}程序运行结果:面值为 :1的最小硬币数 : 1 硬币为:1 面值为 :2的最小硬币数 : 2 硬币为:1 1 面值为 :3的最小硬币数 : 3 硬币为:1 1 1 面值为 :4的最小硬币数 : 4 硬币为:1 1 1 1 面值为 :5的最小硬币数 : 1 硬币为:5 面值为 :6的最小硬币数 : 2 硬币为:5 1 面值为 :7的最小硬币数 : 3 硬币为:5 1 1 面值为 :8的最小硬币数 : 4 硬币为:5 1 1 1 面值为 :9的最小硬币数 : 5 硬币为:5 1 1 1 1 面值为 :10的最小硬币数 : 1 硬币为:10 面值为 :11的最小硬币数 : 2 硬币为:10 1 面值为 :12的最小硬币数 : 3 硬币为:10 1 1 面值为 :13的最小硬币数 : 4 硬币为:10 1 1 1 面值为 :14的最小硬币数 : 5 硬币为:10 1 1 1 1 面值为 :15的最小硬币数 : 2 硬币为:10 5 面值为 :16的最小硬币数 : 3 硬币为:10 5 1 面值为 :17的最小硬币数 : 4 硬币为:10 5 1 1 面值为 :18的最小硬币数 : 5 硬币为:10 5 1 1 1 面值为 :19的最小硬币数 : 6 硬币为:10 5 1 1 1 1 面值为 :20的最小硬币数 : 2 硬币为:10 10 面值为 :21的最小硬币数 : 1 硬币为:21 面值为 :22的最小硬币数 : 2 硬币为:21 1 面值为 :23的最小硬币数 : 3 硬币为:21 1 1 面值为 :24的最小硬币数 : 4 硬币为:21 1 1 1 面值为 :25的最小硬币数 : 1 硬币为:25 面值为 :26的最小硬币数 : 2 硬币为:25 1 面值为 :27的最小硬币数 : 3 硬币为:25 1 1 面值为 :28的最小硬币数 : 4 硬币为:25 1 1 1 面值为 :29的最小硬币数 : 5 硬币为:25 1 1 1 1 面值为 :30的最小硬币数 : 2 硬币为:25 5 面值为 :31的最小硬币数 : 2 硬币为:21 10 面值为 :32的最小硬币数 : 3 硬币为:21 10 1 面值为 :33的最小硬币数 : 4 硬币为:21 10 1 1 面值为 :34的最小硬币数 : 5 硬币为:21 10 1 1 1 面值为 :35的最小硬币数 : 2 硬币为:25 10 面值为 :36的最小硬币数 : 3 硬币为:25 10 1 面值为 :37的最小硬币数 : 4 硬币为:25 10 1 1 面值为 :38的最小硬币数 : 5 硬币为:25 10 1 1 1 面值为 :39的最小硬币数 : 6 硬币为:25 10 1 1 1 1 面值为 :40的最小硬币数 : 3 硬币为:25 10 5 面值为 :41的最小硬币数 : 3 硬币为:21 10 10 面值为 :42的最小硬币数 : 2 硬币为:21 21 面值为 :43的最小硬币数 : 3 硬币为:21 21 1 面值为 :44的最小硬币数 : 4 硬币为:21 21 1 1 面值为 :45的最小硬币数 : 3 硬币为:25 10 10 面值为 :46的最小硬币数 : 2 硬币为:25 21 面值为 :47的最小硬币数 : 3 硬币为:25 21 1 面值为 :48的最小硬币数 : 4 硬币为:25 21 1 1 面值为 :49的最小硬币数 : 5 硬币为:25 21 1 1 1 面值为 :50的最小硬币数 : 2 硬币为:25 25 面值为 :51的最小硬币数 : 3 硬币为:25 25 1 面值为 :52的最小硬币数 : 3 硬币为:21 21 10 面值为 :53的最小硬币数 : 4 硬币为:21 21 10 1 面值为 :54的最小硬币数 : 5 硬币为:21 21 10 1 1 面值为 :55的最小硬币数 : 3 硬币为:25 25 5 面值为 :56的最小硬币数 : 3 硬币为:25 21 10 面值为 :57的最小硬币数 : 4 硬币为:25 21 10 1 面值为 :58的最小硬币数 : 5 硬币为:25 21 10 1 1 面值为 :59的最小硬币数 : 6 硬币为:25 21 10 1 1 1 面值为 :60的最小硬币数 : 3 硬币为:25 25 10 面值为 :61的最小硬币数 : 4 硬币为:25 25 10 1 面值为 :62的最小硬币数 : 4 硬币为:21 21 10 10 面值为 :63的最小硬币数 : 3 硬币为:21 21 21 面值为 :64的最小硬币数 : 4 硬币为:21 21 21 1 面值为 :65的最小硬币数 : 4 硬币为:25 25 10 5

仿佛松树就是一位威风的将军,守护着国家的国民。

动态规划求解硬币找零问题

相关文章:

你感兴趣的文章:

标签云: