问题描述

假设我们有8种不同面值的硬币{1,2,5,10,20,50,100,200},用这些硬币组合够成一个给定的数值n。例如n=200,那么一种可能的组合方式为 200 = 3 * 1 + 1*2 + 1*5 + 2*20 + 1 * 50 + 1 * 100. 问总过有多少种可能的组合方式? (这道题目来自著名编程网站ProjectEuler, 点击这里查看原题目) 类似的题目还有:

[华为面试题] 1分2分5分的硬币三种,组合成1角,共有多少种组合

[创新工厂笔试题] 有1分,2分,5分,10分四种硬币,每种硬币数量无限,给定n分钱,有多少中组合可以组成n分钱

问题分析

给定一个数值sum,假设我们有m种不同类型的硬币{V1, V2, ..., Vm},如果要组合成sum,那么我们有

sum = x1 * V1 + x2 * V2 + ... + xm * Vm

求所有可能的组合数,就是求满足前面等值的系数{x1, x2, ..., xm}的所有可能个数。

[思路1] 当然我们可以采用暴力枚举,各个系数可能的取值无非是x1 = {0, 1, ..., sum / V1}, x2 = {0, 1, ..., sum/ V2}等等。这对于硬币种类数较小的题目还是可以应付的,比如华为和创新工厂的题目,但是复杂度也很高O(sum/V1 * sum/V2 * sum/V3 * ...)

[思路2] 从上面的分析中我们也可以这么考虑,我们希望用m种硬币构成sum,根据最后一个硬币Vm的系数的取值为无非有这么几种情况,xm分别取{0, 1, 2, ..., sum/Vm},换句话说,上面分析中的等式和下面的几个等式的联合是等价的。

sum = x1 * V1 + x2 * V2 + ... + 0 * Vm

sum = x1 * V1 + x2 * V2 + ... + 1 * Vm

sum = x1 * V1 + x2 * V2 + ... + 2 * Vm

...

sum = x1 * V1 + x2 * V2 + ... + K * Vm

其中K是该xm能取的最大数值K = sum / Vm。可是这又有什么用呢?不要急,我们先进行如下变量的定义:

dp[i][sum] = 用前i种硬币构成sum 的所有组合数。

那么题目的问题实际上就是求dp[m][sum],即用前m种硬币(所有硬币)构成sum的所有组合数。在上面的联合等式中:当xn=0时,有多少种组合呢? 实际上就是前i-1种硬币组合sum,有dp[i-1][sum]种! xn = 1 时呢,有多少种组合? 实际上是用前i-1种硬币组合成(sum - Vm)的组合数,有dp[i-1][sum -Vm]种; xn =2呢, dp[i-1][sum - 2 * Vm]种,等等。所有的这些情况加起来就是我们的dp[i][sum]。所以:

dp[i][sum] = dp[i-1][sum - 0*Vm] + dp[i-1][sum - 1*Vm]

+ dp[i-1][sum - 2*Vm] + ... + dp[i-1][sum - K*Vm]; 其中K = sum / Vm

换一种更抽象的数学描述就是:

通过此公式,我们可以看到问题被一步步缩小,那么初始情况是什么呢?如果sum=0,那么无论有前多少种来组合0,只有一种可能,就是各个系数都等于0;

dp[i][0] = 1   // i = 0, 1, 2, ... , m

如果我们用二位数组表示dp[i][sum], 我们发现第i行的值全部依赖与i-1行的值,所以我们可以逐行求解该数组。如果前0种硬币要组成sum,我们规定为dp[0][sum] = 0.

程序源码

通过上面的讨论,我们最终可以写出下面的代码来求解该类问题:

1 /*

2 * Filename :coins.cpp3 * Description: solve coin combinations using dynamic programing4 * Complier: g++5 * Author: python276 */

7 #include

8 #include

9 #include

10 #include

11

12 using namespacestd;13

14 /****************************************************************15 * coin Combinations: using dynamic programming16 *17 * Basic idea:18 * dp[i][j] = sum(dp[i-1][j-k*coins[i-1]]) for k = 1,2,..., j/coins[i-1]19 * dp[0][j] = 1 for j = 0, 1, 2, ..., sum20 *21 * Input:22 * coins[] - array store all values of the coins23 * coinKinds - how many kinds of coins there are24 * sum - the number you want to construct using coins25 *26 * Output:27 * the number of combinations using coins construct sum28 *29 * Usage:30 * c[3] = {1, 2, 5};31 * int result = coinCombinations(c, 3, 10);32 *33 ****************************************************************/

34 int coinCombinations(int coins[], int coinKinds, intsum)35 {36 //2-D array using vector: is equal to: dp[coinKinds+1][sum+1] = {0};

37 vector > dp(coinKinds + 1);38 for (int i = 0; i <= coinKinds; ++i)39 {40 dp[i].resize(sum + 1);41 }42 for (int i = 0; i <= coinKinds; ++i)43 {44 for (int j = 0; j <= sum; ++j)45 {46 dp[i][j] = 0;47 }48 }49

50 //init: dp[i][0] = 1; i = 0, 1, 2 ..., coinKinds51 //Notice: dp[0][0] must be 1, althongh it make no sense that52 //using 0 kinds of coins construct 0 has one way. but it the foundation53 //of iteration. without it everything based on it goes wrong

54 for (int i = 0; i <= coinKinds; ++i)55 {56 dp[i][0] = 1;57 }58

59 //iteration: dp[i][j] = sum(dp[i-1][j - k*coins[i-1]])60 //k = 0, 1, 2, ... , j / coins[i-1]

61 for (int i = 1; i <= coinKinds; ++i)62 {63 for (int j = 1; j <= sum; ++j)64 {65 dp[i][j] = 0;66 for (int k = 0; k <= j / coins[i-1]; ++k)67 {68 dp[i][j] += dp[i-1][j - k * coins[i-1]];69 }70 }71 }72

73 returndp[coinKinds][sum];74 }75

76 intmain()77 {78 int coins[8] = {1, 2, 5, 10, 20, 50, 100, 200};79 int sum = 200;80 int result = coinCombinations(coins, 8, 200);81 cout << "using 8 kinds of coins construct 200, combinations are:" <

聪明的读者或许已经发现,在算法的描述中说明用动态规划的方法来求解此问题,什么?动态规划,我们什么时候用动态规划了?哈哈,在我们写出递归公式并且给出初始解的时候,我们就已经在用动态规划了。

动态规划的基本思想就是将待求解问题分解为若干子问题,(如本题中我们将dp[i][j]分解为若干dp[i-1][j-x]的问题),先求解这些子问题并将结果保存起来( 我们用dp[][]二维数组保存子结果),若在求解较大的问题时用到较小子问题的结果,可以直接取用(求dp[i][j]时用dp[i-1][x]的结果),从而免去重复计算。动态规划是一种非常强大的算法思想,无论做过多少动态规划的题目,下一次依然会被动态规划的强大所震撼。随后的博客中,我们会更多的接触动态规划。你可以在后面的参考文献中找到更多有用的资源。

参考文献

[3] Sanjoy Dasgupta. 算法概论. 清华大学出版社,2008: 173 - 193.

[4] Thomas H. Cormen, et al. 算法导论. 机械工业出版社,2011: 192 - 212.

硬币兑换python 每个面值有多个_【算法27】硬币面值组合问题相关推荐

  1. python数组和链表的区别_算法图解2 - 数组和链表

    原标题:算法图解2 - 数组和链表 我们继续上文的脚步,深入了解一下数组和链表.掌握它们之间的区别和联系,以及各自的使用场景,为后续的算法学习打好基础. 一.计算机内存的工作原理 为了更好的理解数组和 ...

  2. python 正整数 连续多个数之和_[算法]正整数分解为几个连续自然数之和

    题目:输入一个正整数,若该数能用几个连续正整数之和表示,则输出所有可能的正整数序列. 一个正整数有可能可以被表示为n(n>=2)个连续正整数之和,如: 15=1+2+3+4+5 15=4+5+6 ...

  3. 硬币兑换方案总数(动态规划)

    视频链接:求硬币兑换的方案数的分析步骤_哔哩哔哩_bilibili ①题目: ②思路: ③代码(DP数组右下角的那个数就是方案数): import pandas as pd# S = 4 # coin ...

  4. java 算法提高 邮票面值设计 蓝桥杯1046

    java 算法提高 邮票面值设计 蓝桥杯1046 算法提高 邮票面值设计 思路 代码 算法提高 邮票面值设计 Description 给定一个信封,最多只允许粘贴N张邮票,计算在给定K(N+K≤13) ...

  5. python换硬币_Python 硬币兑换问题

    硬币兑换问题: 给定总金额为A的一张纸币,现要兑换成面额分别为a1,a2,....,an的硬币,且希望所得到的硬币个数最少. # 动态规划思想 dp方程式如下 # dp[0] = 0 # dp[i] ...

  6. 程序员的算法趣题Q05: 硬币兑换

    目录 1. 问题描述 2. 递推表达式 3. 代码及测试 1. 问题描述 本题来自<程序员的算法趣题>中的第5题. 公交车上的零钱兑换机可以将纸币兑换成10日元.50日元.100日元和50 ...

  7. python123外汇兑换程序_Python 硬币兑换问题

    硬币兑换问题: 给定总金额为A的一张纸币,现要兑换成面额分别为a1,a2,....,an的硬币,且希望所得到的硬币个数最少. # 动态规划思想 dp方程式如下 # dp[0] = 0 # dp[i] ...

  8. /* * 编程题第四题(20分): 用1元5角钱人名币兑换5分、2分和1分的硬币(每一种都要有)共一百枚,问共有几种兑换方案?并输出每种方案。 */

    题目: /* 编程题第四题(20分): 用1元5角钱人名币兑换5分.2分和1分的硬币(每一种都要有)共一百枚,问共有几种兑换方案?并输出每种方案. */ 我使用java做的 public class ...

  9. 称硬币问题-Python

    称硬币问题-Python: 一. 问题描述 现在有27枚硬币,其中有一枚假币,假币跟真币长得一摸一样,但是稍微重一些.摆在桌上有一个称重天平,要求用最小的次数找出假币,并写出算法代码. 二. 解题思路 ...

最新文章

  1. Canal安装说明(Windows版)
  2. VS创建props属性表并在新项目中导入props属性表
  3. tcpdump过滤某个端口
  4. Py之utils:utils库的简介、安装、使用方法之详细攻略
  5. 温州大学《机器学习》课程课件(九、支持向量机)
  6. Vue.js-Day04-AM【父子组件通信(父传子、子传父)、动态组件、组件的生命周期、动画】
  7. Eclipse中看java源代码
  8. 数据库笔记(数据库操作)
  9. 电脑屏幕卡住了按什么都没反应_手机突然“死机”了关机也不行,怎么按都没反应,怎么办?...
  10. 服务器显卡驱动修复,AMD发布2020年首个显卡驱动:一口气修复27个Bug
  11. 如何实现全选checkbox效果
  12. Myeclipse2017破解:成功解决me Trial expired 0 days ago mgeclipse It's now time to buy the best IDE for yo
  13. 60分钟企业经营战略
  14. 如果APP推广月预算只有10万 该如何展开推广?
  15. flutter - 图文讲解表单组件基本使用 注册实战
  16. 用FPGA实现半加器
  17. 关于1NF、2NF、3NF、BCNF的常考判定
  18. 计算机毕业设计JAVA图书个性化推荐系统mybatis+源码+调试部署+系统+数据库+lw
  19. 新基建时代的云生态 ——中国软件网“曹云社”走进浪潮
  20. java添加窗体中_java中利用JFrame创建窗体 【转】

热门文章

  1. 蚂蚁金服数据库性能超甲骨文引热议;三大运营商 5G 预约用户接近 930 万;苹果发布新版 Mac 系统| 极客头条...
  2. 告别枯燥,这本插画式 Python 书难怪销量 70W+
  3. Google 击败苹果!| 极客头条
  4. 为什么我坚持用Go语言做Web应用开发框架?
  5. GPL侵权诉讼被驳回,Linux之父Torvalds又要发飙了!
  6. 为什么量子计算会对我们产生威胁?
  7. linux服务器分区方案
  8. Maven 国内镜像很慢解决的方法
  9. oracle 12514 pl sql,plsql登陆oracle报错!连接Oracle 10g时ORA-12514: TNS: 监听进程不能解析在连接描述符中给出的...
  10. 加密+拜占庭将军_屡屡被提及拜占庭将军问题,究竟和比特币是什么关系?