多少个硬币加起来等于1美元?

环境:VC++ 6.0 , Win9x, WinNT

在这篇文章里,我介绍了一个解决著名的“多少个硬币组成一美元”问题的可选择方案。这种方法采用的动态规划是一个有名用来解决状态不定问题的方法。典型的动态规划问题包括费波纳契数列和阶乘,这些都把递归作为求解的第一选择。

问题陈述

找出所有的用1美分,5美分,25美分和50美分组成一美元的组合。这个问题的一个流行的版本也包括10美分。这篇文章设法强调有关的算法:动态规划的作用。在文章的结束,很容易明白这种方法能扩展到许多硬币组成一个希望和。

用数学的术语描述:你有N种面值V1, V2, ..., VN的硬币,你要用其中的一种或多种硬币组成和S,每种硬币可以一个或多个。

手工方法

就这个一元问题来说,并非不能用手工的方法求出解。用纸和笔,你可以像这样开始:

1、  有多少种方式只用1美分或5美分或25美分或50美分组成一美元:你需要100个1美分,20个5美分,4个25美分,2个50美分。

2、  有多少种方式用1美分和5美分组成一美元:你需要计算1个5美分时要多少个1美分,2个5美分需要多少个1美分···直到计算19个5美分。

3、  类似的,你还要计算5美分和25美分,25美分和50美分,1美分和25美分,1美分和 50美分。

4、  求用3种硬币组成1美元的方案的数量。从上面的步骤2和步骤3,你知道所有的用两种硬币组成一美元的方案。举个例子,把所有的用1美分和5美分组成1美元的方案放在一边。现在,用他们组成75美分来代替1美元。就像你猜的一样,只需要加上25美分,你就能得到一种方案。让他们组成50美分,在加上2个25美分,你就得到了第二种方案,以此类推。

为了便于理解,看下面的计算:

1

5

95

1

= 100

2

5

90

1

= 100

... ...

... ...

... ...

... ...

... ...

... ...

... ...

... ...

19

5

5

1

= 100

... ...

... ...

... ...

... ...

... ...

... ...

... ...

... ...

1

5

14

5

= 75

+ 1

25

= 100

1

5

9

5

= 50

+ 2

25

= 100

一旦你得到了三种硬币组成一个和的所有方案,你不难得到四种硬币的。只需要见到的重复上面的步骤4。

写完这些,你开始感到疲倦了,但是在仔细和重复的努力后,你能得到正确的解,但是我推荐这种方法。因为,一旦增加一种硬币VN+1 ,就会使问题变成一个庞大的任务。在这点上动态规划能正确的解决这类问题。

动态规划方法:一个公式?

动态规划试着识别状态和改变他们的条件。你需要的一切是一个包含n和n-1的函数(或者根据问题性质,最接近的关系)。

一个有名的动态规划的例子是阶乘,通过递归逐一调用你得到的解。

如果是阶乘,他的函数是:

F (n) = n*f (n - 1)

if n>1

= 1

If n = 1

试着找出与你的问题的相同点。硬币问题要求你从V1, V2, ..., VN 面值的硬币中凑出S。你可以像这样定义它:

S = aV1 + bV2 + cV3 + ... + kVN

 

你需要求出a,b,c,…,k值,把他们与V1, V2, ..., VN 组合在一起,就是所求的组合方案。

状态的识别和转变

因为你打算用动态规划按部就班的执行,先前手工的方法给了你一点暗示,试着像前面那样计算。你总能得到用两种硬币组成一个值的方案。算法类似这样:

For i = 1 to Sum / V1
For j = 1 to Sum / V2
total = i* V1 + j* V2;
If (total == Sum)
   return i V1   j V2;
else
   continue;

上面是蛮力计算法的本质,你最后的解法不是这个,你马上就会知道。在你的代码里,你会优化算法,为了减少循环,分别把上面的循环改为(Sum - V2) / V1 和 (Sum - V1) / V2 。这是因为当你计划和S有V1  ,V2两部分组成,它们必须有一个。

正确,优化后的算法是:

For i = 1 to (Sum - V2 ) / V1
For j = 1 to (Sum - V1 ) / V2
total = i* V1 + j* V2;
If (total == Sum)
   return i V1  j V2;
else
   continue;

你有了有效的第一步:获得所有的用两种硬币组成一个值S的组合。在代码里,函数Comb2()就相当于这个功能。这个函数有两个int型参数a和b,并且求出所有的使a,b加起来等于S的组合。我选择vector<map<int>>作为返回值,和因为我预计有大量的组合,每个组合都有两个(或更多)的面值(keys of map)V1 V2, 和它们需求的数量(values of the map)

Comb2()函数在a=5,b=15,S=100条件吓的一部分输出为:

Output Vector Vect: contains Vect1, Vect2....VectN

Vect1: 5 : 5  25 : 3 --- 5 coins of 5 cents, 3 coins of 25 cents.

Vect2: 5 : 10  2 25 --- 10 coins of 5 cents, 2 coins of 25 cents.

注意:在实际输出中顺序可能不一样。这里只是为了说明问题,这只是显示了矢量的2个元素

现在,在看三种硬币组成100的组合;每种组合都必须包含这三种硬币。用1美分,5美分和25美分举个例子。你已经有了一种方法得到用1美分和5美分组合的所有和。另一种硬币依旧是25美分。你的目标和是100.

所以,把你的目标和改为100-25=75.试着得到所有用1美分和5美分组成75美分的组合。Comb2()会以向量的方式给你解。一旦你得到这个,你只需为每个组合加上25美分就能的到100美分的组合。

接着,把目标和改为100-25*2=50.用Comb2()得到解后,为每个组合加上2个25美分。

这里只有一个25 的空间,所以,将目标和改为100-25*3=25,用Comb2()得到解后,为每个组合加上3个25美分。

正像你上面那样排除了25美分,你值需要重复上述步骤,排除5美分,1美分,就能得到所有的组合。

你得到了所有组合后,你可以用5,25,50;1,25,50;1,5,50重复上述步骤。

这就是函数Comb3()的功能。它有三个int类型的参数a,b,c,求出所a,b,c和为值S的组合。就像Comb2()那样,返回映射的矢量,映射的关键词是硬币的值V1, V2, V3...  映射的值为各个硬币所需数量a.b.c…

这样就能衍生出更高的层次。Comb4()就是用Comb3()生成的组合用上面的方法,求出用四种硬币组成定值的组合。这个可以泛型化的使用递归,但是对这伤脑筋的问题,我限制自己求到4种硬币的组合。

作为一种动态规划方法的对比,我在代码种加入了蛮力计算法(BruteForceTest())。

在我的测试中,BruteForceTest()在xp和windows 2003 Server上大概需要15+ms。而动态规划几乎是0 ms。重复运行BruteForceTest()使运行时间减少到0ms,但是这要归功与二进制代码写入高速缓存。在硬币种类很多的情况下,动态规划方法肯定可以增加实际履行的好处。

  1. // onedollar.cpp : Defines the entry point for the console application.
  2. #define DO_LOG      //comment this out to turn off result log
  3. #include <ctime>
  4. #include <iostream>
  5. #include<fstream>
  6. #include <iomanip>
  7. #include <sstream>
  8. #include <string>
  9. #include <vector>
  10. #include <map>
  11. #include <deque>
  12. #include <set>
  13. #include <map>
  14. #include <bitset>
  15. #include <valarray>
  16. #include <algorithm>
  17. #include <functional>
  18. #include <numeric>
  19. #include <complex>
  20. #include <utility>
  21. #include <cstdio>
  22. #include <cstdlib>
  23. #include <cstring>
  24. #include <cctype>
  25. #include <cmath>
  26. #include <queue>
  27. #include <iosfwd>
  28. #include <conio.h>
  29. using namespace std;
  30. int a[] = {1,5,25,50};
  31. void printvect(vector <map<int, int> > &vect)
  32. {
  33. for (int i=0; i<vect.size(); i++)
  34. {
  35. map<int, int>::const_iterator iter;
  36. for(iter = vect[i].begin(); iter != vect[i].end(); iter++)
  37. cout << (*iter).first << " : " << (*iter).second <<"  ";
  38. cout<<endl;
  39. }
  40. cout<<"/r/nTotal No of combinations:"<<vect.size()<<endl;
  41. }
  42. void logvect(vector <map<int, int> > &vect, const char * filename)
  43. {
  44. ofstream outfile(filename, ios_base::app);
  45. for (int i=0; i<vect.size(); i++)
  46. {
  47. map<int, int>::const_iterator iter;
  48. for(iter = vect[i].begin(); iter != vect[i].end(); iter++)
  49. outfile<<(*iter).first << " : " << (*iter).second <<"  ";
  50. outfile<<"/r/n"<<endl;
  51. }
  52. outfile<<"/r/nTotal No of combinations:"<<vect.size()<<endl;
  53. outfile.close ();
  54. }
  55. void logtext(const char * message, const char * filename)
  56. {
  57. ofstream outfile(filename);
  58. outfile<<message<<endl;
  59. outfile.close ();
  60. }
  61. void appendvect(vector <map<int, int> > &dest, vector <map<int, int> > &src)
  62. {
  63. for (int i=0; i<src.size(); i++)
  64. dest.push_back (src[i]);
  65. }
  66. vector<map<int, int> > BruteForceTest(int a, int b, int c, int d, int S)
  67. {
  68. vector<map<int, int> > retVect;
  69. int w, x, y, z;
  70. int count = 0;
  71. for (w=0; w<=S/c; w++)
  72. {
  73. for (x=0; x<=S/c; x++)
  74. {
  75. for (y=0; y<=S/b; y++)
  76. {
  77. for (z=0; z<=S/a; z++)
  78. {
  79. int val= w*d + x*c + y*b + z*a;
  80. if (val == 100)
  81. {
  82. map<int, int> temp;
  83. temp[a] = z;
  84. temp[b] = y;
  85. temp[c] = x;
  86. temp[d] = w;
  87. retVect.push_back(temp);
  88. }
  89. }
  90. }
  91. }
  92. }
  93. return retVect;
  94. }
  95. vector<map<int, int> > Comb1(int a, int S)
  96. {
  97. vector<map<int, int> > retVect;
  98. map<int, int> temp;
  99. temp[a] = (int)S/a;
  100. retVect.push_back (temp);
  101. return retVect;
  102. }
  103. vector<map<int, int> > Comb2(int a, int b, int S)
  104. {
  105. int divisorA = (S-b)/a;     //for 5, 25 and Sum = 100, we take at least 1 25 and rest of 5s...
  106. int divisorB = (S-a)/b;
  107. vector<map<int, int> > retVect;
  108. for (int i=1; i<= divisorA; i++)
  109. {
  110. for (int j=1; j<= divisorB; j++)
  111. {
  112. int val = a*i + b*j;
  113. if (val == S)
  114. {
  115. map<int, int> temp;
  116. temp[a] = i;
  117. temp[b] = j;
  118. retVect.push_back (temp);
  119. break;
  120. }
  121. }
  122. }
  123. return retVect;
  124. }
  125. vector<map<int, int> > Comb3(int a, int b, int c, int S)
  126. {
  127. vector<map<int, int> > retVect;
  128. int count=0;
  129. for (count=1; count<(S/c); count++)
  130. {
  131. vector<map<int, int> > temp1 = Comb2(a, b, S-(count*c));
  132. //for temp1, append c count
  133. for (int i=0; i<temp1.size(); i++)
  134. temp1[i] [c] = count;
  135. appendvect(retVect, temp1);
  136. }
  137. for (count=1; count<(S/c); count++)
  138. {
  139. vector<map<int, int> > temp2 = Comb2(b, c, S-(count*a));
  140. //for temp2, append a count
  141. for (int i=0; i<temp2.size(); i++)
  142. temp2[i][a] = count;
  143. appendvect(retVect, temp2);
  144. }
  145. for (count=1; count<(S/c); count++)
  146. {
  147. vector<map<int, int> > temp3 = Comb2(c, a, S-(count*b));
  148. //for temp3, append b count
  149. for (int i=0; i<temp3.size(); i++)
  150. temp3[i][b] = count;
  151. appendvect(retVect, temp3);
  152. }
  153. //now remove duplicates from the resultant vector.
  154. std::sort(retVect.begin(), retVect.end());
  155. retVect.erase(std::unique(retVect.begin(), retVect.end()), retVect.end());
  156. return retVect;
  157. }
  158. vector<map<int, int> > Comb4(int a, int b, int c, int d, int S)
  159. {
  160. vector<map<int, int> > retVect;
  161. int count=0;
  162. for (count=1; count<(S/d); count++)
  163. {
  164. vector<map<int, int> > temp1 = Comb3(a, b, c, S-(count*d));
  165. //for temp1, append d count
  166. for (int i=0; i<temp1.size(); i++)
  167. temp1[i] [d] = count;
  168. appendvect(retVect, temp1);
  169. }
  170. for (count=1; count<(S/a); count++)
  171. {
  172. vector<map<int, int> > temp2 = Comb3(b, c, d, S-(count*a));
  173. //for temp2, append a count
  174. for (int i=0; i<temp2.size(); i++)
  175. temp2[i] [a] = count;
  176. appendvect(retVect, temp2);
  177. }
  178. for (count=1; count<(S/b); count++)
  179. {
  180. vector<map<int, int> > temp3 = Comb3(c, d, a, S-(count*b));
  181. //for temp3, append b count
  182. for (int i=0; i<temp3.size(); i++)
  183. temp3[i] [b] = count;
  184. appendvect(retVect, temp3);
  185. }
  186. for (count=1; count<(S/c); count++)
  187. {
  188. vector<map<int, int> > temp4 = Comb3(d, a, b, S-(count*c));
  189. //for temp4, append c count
  190. for (int i=0; i<temp4.size(); i++)
  191. temp4[i] [c] = count;
  192. appendvect(retVect, temp4);
  193. }
  194. //now remove duplicates from the resultant vector.
  195. std::sort(retVect.begin(), retVect.end());
  196. retVect.erase(std::unique(retVect.begin(), retVect.end()), retVect.end());
  197. return retVect;
  198. }
  199. int main()
  200. {
  201. clock_t start,finish;
  202. double time;
  203. start = clock();
  204. vector <map<int, int> > v1 = Comb4(1, 5, 25, 50, 100);
  205. vector <map<int, int> > v2 = Comb3(1, 5, 25, 100);
  206. vector <map<int, int> > v3 = Comb3(5, 25, 50, 100);
  207. vector <map<int, int> > v4 = Comb3(25, 50, 1, 100);
  208. vector <map<int, int> > v5 = Comb3(50, 1, 5, 100);
  209. vector <map<int, int> > v6 = Comb2(1, 5, 100);
  210. vector <map<int, int> > v7 = Comb2(1, 25, 100);
  211. vector <map<int, int> > v8 = Comb2(1, 50, 100);
  212. vector <map<int, int> > v9 = Comb2(5, 25, 100);
  213. vector <map<int, int> > v10 = Comb2(5, 50, 100);
  214. vector <map<int, int> > v11 = Comb2(25, 50, 100);
  215. vector <map<int, int> > v12 = Comb1(1, 100);
  216. vector <map<int, int> > v13 = Comb1(5, 100);
  217. vector <map<int, int> > v14 = Comb1(25, 100);
  218. vector <map<int, int> > v15 = Comb1(50, 100);
  219. vector <map<int, int> > v;
  220. appendvect(v, v1);
  221. appendvect(v, v2);
  222. appendvect(v, v3);
  223. appendvect(v, v4);
  224. appendvect(v, v5);
  225. appendvect(v, v6);
  226. appendvect(v, v7);
  227. appendvect(v, v8);
  228. appendvect(v, v9);
  229. appendvect(v, v10);
  230. appendvect(v, v11);
  231. appendvect(v, v12);
  232. appendvect(v, v13);
  233. appendvect(v, v14);
  234. appendvect(v, v15);
  235. finish = clock();
  236. time = (double(finish)-double(start));
  237. cout<<"Through Combination algorithm, the result is: /r/n"<<endl;
  238. cout<<"Total Time taken: "<<time<<" ms. Press any key to view the result.../r/n"<<endl;
  239. getch();
  240. printvect (v);
  241. #ifdef DO_LOG
  242. logtext("By Combination Algorithm, the result is:/r/n", "logresult.txt");
  243. logvect(v, "logresult.txt");
  244. #endif
  245. //Applying Brute Force Approach to verify the result and compare the time.
  246. cout<<"Press B or b to try BFS.../r/n"<<endl;
  247. int c = getch();
  248. if ('b' != c && 'B' != c)
  249. return 0;
  250. cout<<"By Brute Force technique, the result is: /r/n"<<endl;
  251. vector <map<int, int> > vBruteForce;
  252. start = finish = 0;
  253. start = clock();
  254. vBruteForce = BruteForceTest(1, 5, 25, 50, 100);
  255. finish = clock();
  256. time = (double(finish)-double(start));
  257. cout<<"Total Time taken for BFS: "<<time<<" ms. Press any key to view the result.../r/n"<<endl;
  258. getch();
  259. printvect(vBruteForce);
  260. cout<<"/r/nTotal No of combinations through BFS:"<<vBruteForce.size()<<"/r/n"<<endl;
  261. cout<<"Press any key to end..."<<endl;
  262. #ifdef DO_LOG
  263. logtext("By Brute Force technique, the result is:/r/n", "BFSLogresult.txt");
  264. logvect(vBruteForce, "BFSLogresult.txt");
  265. #endif
  266. getch();
  267. return 0;
  268. }

原文地址:http://www.codeguru.com/cpp/cpp/algorithms/combinations/article.php/c15409/

动态规划:和组成的问题相关推荐

  1. 伍六七带你学算法 动态规划 ——不同路径

    力扣 62. 不同路径 难度 中等 一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为"Start" ). 机器人每次只能向下或者向右移动一步.机器人试图达到网格 ...

  2. 由动态规划计算编辑距离引发的思考

    简单介绍 编辑距离算法: https://www.cnblogs.com/BlackStorm/p/5400809.html https://wizardforcel.gitbooks.io/the- ...

  3. LeetCode 10. Regular Expression Matching python特性、动态规划、递归

    前言 本文主要提供三种不同的解法,分别是利用python的特性.动态规划.递归方法解决这个问题 使用python正则属性 import reclass Solution2:# @return a bo ...

  4. 【动态规划】Part1

    1. 硬币找零 题目描述:假设有几种硬币,如1.3.5,并且数量无限.请找出能够组成某个数目的找零所使用最少的硬币数. 分析:   dp [0] = 0            dp [1] = 1 + ...

  5. 2016.4.2 动态规划练习--讲课整理

    1.codevs1742 爬楼梯  时间限制: 1 s  空间限制: 128000 KB  题目等级 : 黄金 Gold 题目描述 Description 小明家外面有一个长长的楼梯,共N阶.小明的腿 ...

  6. 算法设计与分析第4章 动态规划(二)【DP序列问题】

    第3章 动态规划(二)[DP序列问题] 3.2 DP序列问题 (51nod的动态规划教程很不错,讲解很详细,以下分析来自51nod) 1.矩阵取数问题 给定一个m行n列的矩阵,矩阵每个元素是一个正整数 ...

  7. 算法设计与分析第4章 动态规划(一)【背包问题】

    第3章动态规划(一)[背包问题] 基本思想: 动态规划算法与分治法类似,其基本思想也是将待求解问题分解成若干个子问题,但是经分解得到的子问题往往不是互相独立的.不同子问题的数目常常只有多项式量级.在用 ...

  8. ADPRL - 近似动态规划和强化学习 - Note 7 - Approximate Dynamic Programming

    Note 7 - 近似动态规划 Approximate Dynamic Programming 7. 近似动态规划 (Approximate Dynamic Programming) 7.1 近似架构 ...

  9. ADPRL - 近似动态规划和强化学习 - Note 6 - Mitigating the Curse of Dimensionality

    Note 6 Mitigating the Curse of Dimensionality 减轻维度诅咒 6. Mitigating the Curse of Dimensionality 减轻维度诅 ...

  10. ADPRL - 近似动态规划和强化学习 - Note 5 - Banach Fixed Point Theorem in Dynamic Programming

    动态规划中的巴拿赫不动点定理 5. Banach Fixed Point Theorem in Dynamic Programming 5.1 巴拿赫不动点定理定理 (Banach fixed poi ...

最新文章

  1. jmeter http并发测试时报错
  2. koa2使用注意点总结
  3. 【转】TCP、UDP数据包大小的限制
  4. 递归 将一个整数逆序
  5. 4.12L.02_CT Makefile
  6. python 数据结构转换层_python – 具有Maxpooling1D和channel_first的Keras模型
  7. django 1.8 官方文档翻译: 3-2-3 TemplateResponse 和 SimpleTemplateResponse
  8. php 指定时间之前,php计算给定时间之前的函数用法实例
  9. python爬虫从小白到高手 Day1 爬取百度音乐歌单
  10. zabbix替代smokeping的解决方案
  11. ARouter 源码历险记 (二)
  12. mysql cst_JDBC与mysql同为CST时区导致数据库时间和客户端时间差13或者14小时
  13. python编译器大全_Python编译器
  14. idea 修改前后端代码自动运行
  15. linux 安装 yum
  16. plupload文件上传插件
  17. 俄罗斯、乌克兰程序员薪资大曝光!(来长长见识)
  18. 银河土星_不要购买三星银河笔记20
  19. 群体智能与进化计算_液态大脑与固态大脑——圣塔菲最新群体智能文集
  20. Thinking in Java之吸血鬼数字

热门文章

  1. 爬虫进阶——解决封IP问题| 部署ADSL服务器获取动态IP
  2. editplus-edtools.rar格式化插件安装详细方法
  3. 暑假学习计划回顾总结一
  4. 亚马逊html在线编辑器,在线纯文字写作编辑器工具
  5. java工程师培训班,Java篇
  6. 成都传智播客hibernate视频下载及讲师介绍
  7. win7 蓝屏 0x000000c5
  8. HTML中常用的颜色词汇
  9. 如何将Kwikset Kevo恢复出厂设置
  10. 报名成功后,我和研友去做了这件事!