一,问题描述

给定一组硬币数,找出一组最少的硬币数,来找换零钱N。

比如,可用来找零的硬币为: 1、3、4   待找的钱数为 6。用两个面值为3的硬币找零,最少硬币数为2。而不是 4,1,1

因此,总结下该问题的特征:①硬币可重复多次使用。②在某些情况下,该问题可用贪心算法求解。具体可参考:某种 找换硬币问题的贪心算法的正确性证明

二,动态规划分析

为了更好的分析,先对该问题进行具体的定义:将用来找零的硬币的面值存储在一个数组中。如下:

coinsValues[i] 表示第 i 枚硬币的面值。比如,

第 i 枚硬币     面值

1                1

2                3

3                4

待找零的钱数为 n (上面示例中 n=6)

为了使问题总有解,一般第1枚硬币的面值为1

考虑该问题的最优子结构:设 c[i,j]表示 可用第 0,1,.... i 枚硬币 对 金额为 j 的钱 进行找钱 所需要的最少硬币数。

i 表示可用的硬币种类数, j 表示 需要找回的零钱

第 i 枚硬币有两种选择:用它来找零 和 不用它找零。因此,c[i,j]的最优解如下:

c[i,j]= min{c[i-1,j] , c[i, j-coinsValues[i]] + 1}   其中,

c[i-1,j] 表示 使用第 i 枚硬币找零时,对金额为 j 进行找钱所需要的最少硬币数

c[i, j-coinsValues[i]] + 1 表示 使用 第 i 枚硬币找零时,对金额为 j 进行找钱所需要的最少硬币数。由于用了第 i 枚硬币,故使用的硬币数量要增1

c[i,j] 取二者的较小值的那一个。

另外,对特殊情况分析(特殊情况1)一下:

c[0][j]=Integer.MAXVALUE ,因为 对 金额为 j 的钱找零,但是可用的硬币面值 种类为0,这显然是无法做到的嘛(除非是强盗:) )

其实这是一个”未定义“的状态。它之所以初始为Integer.MAXVALUE,与《背包九讲》中的”当要求背包必须装满的条件下,价值尽可能大“时 的初始化方式一样。

c[i][0]=0,因为,对 金额为0的钱 找零,可用来找零的硬币种类有 i 种,金额为0啊,怎么找啊,找个鸭蛋啊。

其实,边界条件是根据具体的问题而定的。DP太博大了,理解的不深。

另外,还有一种特殊情况(特殊情况2),就是: coinsValues[i] > j

这说明第 i 枚硬币的面值大于 金额 j ,这也不能用 第 i 枚硬币来找零啊,(欠你5块钱,但是找你一张百元大钞)不然就亏了了(吃亏是福啊^~^...or 杀鸡焉用牛刀!!!)

有了这个特征转移方程:c[i,j]= min{c[i-1,j] , c[i, j-coinsValues[i]] + 1}  ,就好写代码了。

三,JAVA代码实现

 1     /**
 2      *
 3      * @param coinsValues 可用来找零的硬币 coinsValues.length是硬币的种类
 4      * @param n 待找的零钱
 5      * @return 最少硬币数目
 6      */
 7     public static int charge(int[] coinsValues, int n){
 8         int[][] c = new int[coinsValues.length + 1][n + 1];
 9
10         //特殊情况1
11         for(int i = 0; i <= coinsValues.length; i++)
12             c[i][0] = 0;
13         for(int i = 0; i <= n; i++)
14             c[0][i] = Integer.MAX_VALUE;
15
16         for(int j_money = 1; j_money <=n; j_money++)
17         {
18
19             for(int i_coinKinds = 1; i_coinKinds <= coinsValues.length; i_coinKinds++)
20             {
21                 if(j_money < coinsValues[i_coinKinds-1])//特殊情况2,coinsValues数组下标是从0开始的,  c[][]数组下标是从1开始计算的
22                 {
23                     c[i_coinKinds][j_money] = c[i_coinKinds - 1][j_money];//只能使用 第 1...(i-1)枚中的硬币
24                     continue;
25                 }
26
27                 //每个问题的选择数目---选其中较小的
28                 if(c[i_coinKinds - 1][j_money] < (c[i_coinKinds][j_money - coinsValues[i_coinKinds-1]] +1))
29                     c[i_coinKinds][j_money] = c[i_coinKinds - 1][j_money];
30                 else
31                     c[i_coinKinds][j_money] = c[i_coinKinds][j_money - coinsValues[i_coinKinds-1]] +1;
32             }
33         }
34         return c[coinsValues.length][n];
35     }

①第28行-20行 就是状态转换方程的表示。

②第16行-第19行的for循环体现就是动态规划的自底向上的思想。

复杂度分析:从代码19-20行的for循环来看,时间复杂度为O(MN),M为可用的硬币种类数目,N为待找的零钱金额

从理论上分析,DP(Dynamic Programming)的时间复杂度为子问题的个数乘以每个子问题的可用选择数。显然,这个有MN个子问题,每个子问题有两种选择(选第i枚硬币和不选第i枚硬币)。

一直很好奇DP通过列一个方程就把一个问题给解决了,其实从16-19行的for循环来看,循环的下标是由小到大,说明它先解决子问题,然后再把原问题给解决了。

四,参考资料

硬币找零问题的动态规划实现

某种 找换硬币问题的贪心算法的正确性证明

从 活动选择问题 看动态规划和贪心算法的区别与联系

http://haolloyin.blog.51cto.com/1177454/352115

五,完整代码

import java.util.Arrays;public class DPCharge {/*** * @param coinsValues 可用来找零的硬币 coinsValues.length是硬币的种类* @param n 待找的零钱* @return*/public static int charge(int[] coinsValues, int n){int[][] c = new int[coinsValues.length + 1][n + 1];//特殊情况1for(int i = 0; i <= coinsValues.length; i++)c[i][0] = 0;for(int i = 0; i <= n; i++)c[0][i] = Integer.MAX_VALUE;for(int j_money = 1; j_money <=n; j_money++){for(int i_coinKinds = 1; i_coinKinds <= coinsValues.length; i_coinKinds++){if(j_money < coinsValues[i_coinKinds-1])//特殊情况2
                {c[i_coinKinds][j_money] = c[i_coinKinds - 1][j_money];continue;}//每个问题的选择数目---选其中较小的if(c[i_coinKinds - 1][j_money] < (c[i_coinKinds][j_money - coinsValues[i_coinKinds-1]] +1))c[i_coinKinds][j_money] = c[i_coinKinds - 1][j_money];elsec[i_coinKinds][j_money] = c[i_coinKinds][j_money - coinsValues[i_coinKinds-1]] +1;}}return c[coinsValues.length][n];}public static void main(String[] args) {int[] coinsValues = {1,3,4};Arrays.sort(coinsValues);//需要对数组排序,不然会越界.....int n = 6;int minCoinsNumber = charge(coinsValues, n);System.out.println(minCoinsNumber);}
}

原文:http://www.cnblogs.com/hapjin/p/5578852.html

转载于:https://www.cnblogs.com/hapjin/p/5578852.html

硬币找零问题的动态规划实现相关推荐

  1. 硬币找零问题,动态规划基础,百度面试题

    问题描述:给出几种面值的硬币,要求用这几种硬币找零出所给零钱数,用的硬币数要最少. 过去我们用过贪心法解决此类问题,包括本人在百度面试时,也是用的贪心法(面试官对这个解答不满意),贪心法只适用于硬币特 ...

  2. java动态规划凑硬币问题,详解动态规划最少硬币找零问题--JavaScript实现

    硬币找零问题是动态规划的一个经典问题,其中最少硬币找零是一个变种,本篇将参照上一篇01背包问题的解题思路,来详细讲解一下最少硬币找零问题.如果你需要查看上一篇,可以点击下面链接: 详解动态规划01背包 ...

  3. Python 动态规划(DynamicProgramming)-硬币找零

    动态规划(DynamicProgramming)-硬币找零 文章目录 动态规划(DynamicProgramming)-硬币找零 1.动态规划 a.什么是动态规划 b.适用对象 2.硬币找零-Codi ...

  4. 动态规划——硬币找零思路

    找零的两种问题 硬币找零问题,有两种.一种用贪心解决,一种用动态规划解决. 问题1:假设我们有 v1,v2,--,vn(单位是元)这些币值的硬币,它们的张数分别是 c1.c2.-, cn.我们现在要用 ...

  5. 动态规划实战--硬币找零问题

    上一篇文章上提到硬币找零的例子,现在我们实战动态规划就从硬币找零开始 问题描述: 给定 n 种不同面值的硬币,分别记为 c[0], c[1], c[2], - c[n],同时还有一个总金额 k,编写一 ...

  6. 动态规划——硬币找零和币值最大化问题

    一.硬币找零问题 1.问题 有面值为1元.3元和5元的硬币若干枚,给定一个输入面额,问如何采用最少的硬币数目,得到当前面额 2.思路 找出状态转移方程,每次可以拿取1元.3元或者5元的硬币,每次拿取, ...

  7. 最少硬币找零系列问题(01背包,完全背包,多重背包动态规划)

    背包问题思路解决最小硬币找零系列问题. 一.01硬币找零问题(01背包) 给定不同面额的硬币 coins 和总金额 m.每个硬币最多选择一次.计算可以凑成总金额所需的最少的硬币个数.如果没有任何一种硬 ...

  8. 硬币找零 acm入门 day4--动态规划dp第一题

    A - 硬币找零 在现实生活中,我们经常遇到硬币找零的问题,例如,在发工资时,财务人员就需要计算最少的找零硬币数,以便他们能从银行拿回最少的硬币数,并保证能用这些硬币发工资.我们应该注意到,人民币的硬 ...

  9. 最少硬币找零问题(js实现)

    最少硬币找零问题 例如,有以下面额(硬币):1,5,10,5. 如果要找36的零钱,我们可以用1个25的硬币.1个10的硬币和1个1的硬币. 如何将这个解答转化成算法? 今天我们只要采用两种方法来解决 ...

最新文章

  1. [FZSZOJ 1029] 观察者加强版
  2. 文巾解题 196. 删除重复的电子邮箱
  3. oracle创建dblink语句_一文看懂Oracle12c中多租户(容器)从种子创建PDB
  4. 三分钟让你掌握JavaScript中值传递和引用传递的区别
  5. 基于AVR单片机PWM功能的数控恒流源研制
  6. LeetCode题库5:最长回文子串——JavaScript解答
  7. PyTorch1.4安装(Anaconda3 + Python3.6 + cpu版本)
  8. IDM下载器使用教程
  9. pdf文件怎么合并在一起
  10. Oliver的成绩(score)
  11. 数据库实现递归查询,获取节点的所有子孙节点
  12. php 获取header头信息并显示网址,php 获取远程网址header头信息的方法
  13. Python语言的动态性:运行时动态绑定,删除属性和方法
  14. Wi-Fi PNO扫描流程(Android P)
  15. 获取JOP卡的版本与功能信息
  16. 2834: 小凯的书架
  17. 苹果xr电池容量_5G iPhone 12电池变小了!外媒:苹果认为5nm工艺很节能
  18. jQuery基础二DOM操作
  19. 大数据分析、机器学习、智能化等概念梳理
  20. 网络电视html5软件,网络电视软件哪个好

热门文章

  1. 什么是FTP服务?其主要功能是什么?其传输方式有哪些?
  2. 【Flink异常】Caused by: java.lang.RuntimeException: Row arity of from does not match serializers
  3. 巨坑:数据库查询有结果,而对应的实体类个别为null
  4. Unity 如何与服务器对接来实现
  5. 完美解决 - 前端发版后浏览器缓存问题(发版后及时拉取最新版本代码)
  6. 华硕Z77老主板加持NVMe SSD 成功!
  7. 10.8上海交大PMP每日一题
  8. 月薪5万 微软中国研究院 最新面试题
  9. 创建AWS OrganizationsSCP
  10. context capture如进行空三迁移