凑硬币问题

题目详情为:有面值为1元、3元和5元的硬币若干枚,如何用最少的硬币凑够11元?

  最近在学习一些重要算法,作为五大算法之一的动态规划法,自然要认真学习,这是一道典型的动态规划问题,这里使用动态规划法的思想来解题;

  我们用d(i)=j来表示凑够i元最少需要j个硬币,通过题目,很容易得到:当i=0时,d(0)=0, 表示凑够0元最小需要0个硬币; 当i=1时,只有面值为1元的硬币可用, 因此我们拿起一个面值为1的硬币,接下来只需要凑够0元即可,而这个是已经知道答案的, 即d(0)=0,则有d(1) = d(1 - 1) + 1 = 1,凑够1元最少需要1个硬币,当i = 2时,d(2) = d(2 - 1) + 1= d(1) +1=2, 当i = 3时,d(3) = min{d(3 - 1) + 1 , d(3 - 3) + 1} = min(3, 1) = 1;动态规划算法通常基于一个递推公式及一个或多个初始状态。在这里d(i) 就是状态,通过分析推导的过程,可以得到,针对面值为1,3,5的硬币,可以得到递推公式(状态转移方程)为:

            d(i) = min{ d(i - Vj) + 1} ,i >= Vj。

  在动态规划中,得到了该问题的状态及其状态转移方程,问题已经解决了一大半了,然后,在分析的过程中,并不能一眼就看出递推公式,它需要更多的练习和更多的实践积累的,并不是一朝一夕能做到的,况且动态规划的关键就是找到状态和状态转移方程,那么容易找到,就不是动态规划了,就不是难点了。根据这个公式,我们可以比较轻易的写出实现的代码:

/*@动态规划练习题如果我们有面值为1元、3元和5元的硬币若干枚,如何用最少的硬币凑够11元?
*/#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int DP_leastcoin(const int coin[], int money)
{int *d = (int *)malloc(sizeof(int) * (money + 1));memset(d, 0, sizeof(int) * money);int iterx = 0, itery = 0;int MIN = 0;int result = 0;d[0] = 0;for(iterx = 1; iterx  <= money; iterx++){for(itery = 0; itery < 3 && iterx >= coin[itery]; itery++){if(itery == 0){MIN = d[iterx - coin[itery]] + 1;}else if(MIN > (d[iterx - coin[itery]] + 1)){MIN = (d[iterx - coin[itery]] + 1);}}d[iterx] = MIN;}printf("要凑的钱  MIN\n");for(iterx = 0; iterx <= money; iterx++){printf("序号%-3d : %d\n", iterx, d[iterx]);}result = d[money];free(d);return result;
}
int main(void)
{const int coin[3] = {1, 3, 5};printf("\nThe result is %d \n", DP_leastcoin(coin, 112));return 0;
}

View Code

  在研究凑硬币问题的时候,我把硬币的面值换为2,3,5,然后依旧使用这个状态转移方程,得到的结果是错的,由此也可以知道,状态转移方程是针对某一问题分析得到的,尽管只是修改了硬币的面值,该方程就不再成立了,首先我们要找到问题所在,是什么问题导致了该方程不再适用。

  我们手动分析面值为2,3,5的情况:

    d(0) = 0

    d(1) = 0

    d(2) = 1

    d(3) = 1

    d(4) = 2

    d(5) = 1

    d(6) = 2

    d(7) = 2

    d(8) = 2

    d(9) = 3

    d(10) = 2

    d(11) = 3

  我们先来看看面值为2,3,5的结果图片,看看是在哪里开始出错的:

  找出两者不一样的值,发现是从序号4开始的,d(4) , d(6) , d(9) , d(11) 这几个不同(当然后续还有其他不同的值),把这些状态代入上面的状态转移方程,看看那哪里不对:

    d(4) = min{ d(4 - 3) + 1, d(4 - 2) + 1} = min{ d(1)+1, d(2)+1 }=min(0+1, 1+1) = 1;

  问题来了,本来d(4)应该是等于2,由两个面值额外2的硬币凑成,这里怎么会有1呢?1的由来,是d(1)+1 = 1;d(6)也是有问题的,看下面

    d(6) = min{d(6 - 5)+1, d(6 - 3)+1, d(6 - 2)+1} = min(d(1)+1, d(3)+1, d(4)+1}=min(1, 2, 2);

  问题还是在d(1)上面,至于后面的d(9) 和d(11)是因为使用了错误的d(6)和d(4)才错的,那这个方程的罪魁祸首就是d(1)咯?

  假设我们把d(4) 和 d(6) 都纠正过来,即d(4)= 2, d(6) = 2,那么结果又如何,你可以自己从新列一遍,从d(6)开始,后面都是正确的。这里把我纠正的图片发一下,面值为2,3,5的,我给的需要凑得钱值是112,设一个更大的值,容易排查错误情况:

    

  上面没有把数据全部列出来。我检查了一下,对于面值为2,3,5的情况,没有发现错误的。

  很奇怪,这样一改程序就正确了,我猜想,把面值1,3,5的状态转移方程,拿到面值为2,3,5问题里面就出错,而修改一下(2,3,5)问题的前面某些值d(4)和d(6),状态方程依旧适用,主要原因还是在面值为1的硬币上,由于存在面值为1的情况,假设要凑的钱数为N,那么只要N>0,肯定可以凑出来,把硬币面值换为(2,3,5),那要凑出1块钱是不可能的,所以d(1)+1就有了问题,因为你无法凑到1块钱,是不能使用d(1)的,把存在d(1)的情况去掉,那结果就是正确的,现在知道为什么是4和6了吧,因为你的面值为2,3,5,一个数减掉2,3,5得到1的数就是3,4,6,所以,d(3), d(4) ,d(6)就是错的,那前面怎么没有指出d(3),因为恰好d(1)的结果不影响d(3),尽管如此,还是要把d(1)去掉,方法如下:

d(4) = min{d(4 - 2) + 1}  = min(2) = 2,本来是d(4) = min{d(4 - 2) + 1, d(4 - 3) + 1}  = min(2 , 1) = 1

程序修改十分简单,就在嵌套的for循环里面加上一条语句  “if(iterx - coin[itery] == 1) continue;//当硬币面值没有1时”   即可,如下:

/*@动态规划练习题如果我们有面值为1元、3元和5元的硬币若干枚,如何用最少的硬币凑够11元?
*/#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int DP_leastcoin(const int coin[], int money)
{int *d = (int *)malloc(sizeof(int) * (money + 1));memset(d, 0, sizeof(int) * money);int iterx = 0, itery = 0;int MIN = 0;int result = 0;d[0] = 0;for(iterx = 1; iterx  <= money; iterx++){for(itery = 0; itery < 3 && iterx >= coin[itery]; itery++){if(iterx - coin[itery] == 1) continue;//当硬币面值没有1时if(itery == 0){MIN = d[iterx - coin[itery]] + 1;}else if(MIN > (d[iterx - coin[itery]] + 1)){MIN = (d[iterx - coin[itery]] + 1);}}d[iterx] = MIN;}printf("要凑的钱  MIN\n");for(iterx = 0; iterx <= money; iterx++){printf("序号%-3d : %d\n", iterx, d[iterx]);}result = d[money];free(d);return result;
}
int main(void)
{const int coin[3] = {2, 3, 5};printf("\nThe result is %d \n", DP_leastcoin(coin, 112));return 0;
}

View Code

  啰啰嗦嗦的写了很多,我的表达能力有限,望见谅!此外,希望本博客对大家学习算法能有一点帮助!

  ps:把硬币面值换为其他值,可能要重新分析,这个题目也可以用贪心算法实现,但是貌似贪心算法有可能求出来的是局部最优解,我对贪心算法不大会,等后续学习到了,再来讨论!

转载于:https://www.cnblogs.com/bestDavid/p/DPleastcoin.html

动态规划--凑硬币问题相关推荐

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

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

  2. 动态规划例子,凑硬币,支持各种硬币组合并打印组合详情

    凑硬币是动态规划的一个经典例子,比如有硬币1,3,5,则组合出11有几种方案,最少硬币个数的有几种方案. 网上例子一般都是硬币组合1,3,5,如果不是1开头的就挂了,比如硬币组合是2,5,8,自己编写 ...

  3. 凑硬币(58同城2017校招笔试题)

    凑硬币(58同城2017校招笔试题) 暴力破解,循环递归实现,代码如下: /** * 暴力破解,循环递归,找出了所有可能的组合并进行了存储,* 在循环递归的时候,因为选取的分类相互是有重叠的,生成的递 ...

  4. java最少钱币数_最少钱币数(凑硬币)详解-2-动态规划算法(初窥)-CCF-CSP练习题(100)...

    目录 这篇使用动态规划算法来解决这个问题,借这篇博客初窥动态规划算法.最少钱币数问题也可以看作多重背包问题. 那么什么是动态规划算法? 动态规划(dynamic programming,DP)是运筹学 ...

  5. python-动态规划题目-凑硬币

    题目 文章目录 题目 递归方法 动态规划 首先确定状态 最后一步 分解成子问题 其次确定转移方程 最后确定初始条件和边界情况 两种方法的区别 参考文章 你有三种硬币,面值分别为2元,5元,7元,每种硬 ...

  6. 枚举法 之Java实现凑硬币

    问题? 如何利用1元五元十元凑硬币 Scanner in=new Scanner(System.in);int amout ;amout=in.nextInt();for(int one =0;one ...

  7. Java实现凑硬币或者最少硬币数

    标题:Java实现凑硬币或者最少硬币数 参考视频:参考视频 建议可以看完视频后,再来写代码,或者看这个blog 示例: 作为dp问题求解: public int coinChange(int[] co ...

  8. C语言:凑硬币(两元硬币的组合方案)

    一元硬币的组合 算法思路: 1.一元硬币可以分成1.2.5角等组成 2.可以采用控制变量来进行枚举试验: 3.将可以构成指定元的组合方案进行输出. 代码如下: #include<stdio.h& ...

  9. 穷举法解八皇后问题和凑硬币问题

    八皇后问题 (英文:Eight queens),是由国际西洋棋棋手马克斯·贝瑟尔于1848年提出的问题,是回溯算法的典型案例. 问题表述为:在8×8格的国际象棋上摆放8个皇后,使其不能互相攻击,即任意 ...

  10. 动态规划问题(凑硬币)理解总结

    题目: 假设有 8元,3 元,1 元,5元的硬币若干(无限),现在需要凑出3 元,问有多少种情况可以凑出硬币 动态规划的解题四步骤 步骤参考了csdn上面的讲解,很细致,所以借用这个思路总结 动态规划 ...

最新文章

  1. 路由器运行python脚本_写个Python脚本来登录小米路由器
  2. 3- MySQL数据类型
  3. ChaLearn Gesture Challenge_2:examples体验
  4. 「归纳|总结」程序员必知必会的十大排序算法
  5. python笔试题 github_简单的python面试题,居然
  6. 冯提莫在B站的受欢迎度
  7. 【jupyter notebook】低版本 python 安装jupyter及其问题解决
  8. android数据保存
  9. css居中的几种方法_css两种常用的不定宽高的水平垂直居中方法,记住它,不再为样式发愁...
  10. php工商亮照添加代码,市场监管总局电子营业执照亮照系统上线
  11. Unity与 DLL文件 ☀️| 怎样使用 C# 类库 生成一个DLL文件 并 调用!
  12. Word控件Spire.Doc 转换教程(十六):在 C# 中将 Word 转换为 PDF/A
  13. 安防大数据时代的IP摄像机发展
  14. 百度有啊转型生活平台 启用新标与框计算对接
  15. 3D游戏之父--John Carmack连载系列(四)
  16. 并发之volatile底层原理
  17. 中国IT咨询公司的机会在哪里?
  18. 移动了我的文档,但剩下了一个Documents文件夹,怎么也删不掉,怎么办?
  19. java redis 流水线_Redis附加功能之Redis流水线pipeline
  20. 搭档之家:刘强东章泽天共同成立企业管理公司,前者持股99%

热门文章

  1. C语言定义定长整型数组,C语言变长讯息定义:柔性数组
  2. android 保留edittext中的文字不被后面添加的文字覆盖_【Go语言绘图】图片添加文字(一)...
  3. python函数装饰器有什么作用请列举说明_你必须学写 Python 装饰器的五个理由
  4. python种颜色循环_如何用Python做一个RGB和16进制的互转工具
  5. java--idea-生成jar包的配置
  6. 解释python中的join()和split()函数_python 中join()函数strip() 函数和 split() 函数的详解及实例...
  7. python和go哪个就业前景好_Python和Java就业前景对比
  8. 实现粗糙表面_爬墙吸盘黑科技成真,还能搞定粗糙表面:浙大提出旋转水涡吸附系统...
  9. 如何在Rancher 2.0上快速部署Datadog
  10. 如何发送请求以及AJAX