动态规划之硬币组合问题
问题:如果我们有面值为1元、3元和5元的硬币若干枚,如何用最少的硬币凑够11元?
动态规划的本质是将原问题分解为同性质的若干相同子结构,在求解最优值的过程中将子结构的最优值记录到一个表中以避免有时会有大量的重复计算。
例如硬币组合问题,若求凑够11元的最少硬币数,可以先从凑够0元、1元、2元……的子结构开始分析。
假设d(i)为凑够i元所需最少硬币数,则
d(0) = 0 理所当然
d(1) = 1 要凑够1元,需要从面值小于等于1元的硬币中选择,目前只有面值为1元的硬币
此时d(1) = d(0) + 1
d(2) = d(2 - 1) + 1 = 2, 从面值小于等于2元的硬币中选择,符合要求的硬币面值为:1元。
此时d(2) = d(2-1) + 1
d(3) = d(3 - 3) + 1 = 1, 从面值小于等于3元的硬币中选择,符合要求的硬币面值为:1元,3元。
此时有有两种选择:是否选择含有面值3元的硬币
含有3元硬币:d(3) = d(3 - 3) + 1 = 1
不含3元硬币:d(3) = d(3 - 1) + 1 = d(2) + 1 = 3
自然是选择二者中较小值
依次类推...
就该问题总结一下,随着要凑够钱数的增加:
1.首先要知道所有不大于该钱数的面值,
2.对于每种面值的硬币,求出当选择一个该面值的硬币时所需的硬币数
当选择一个硬币后,所需硬币数+1,所要凑够的钱数=原所要凑的钱数-该硬币面值,所要凑够的钱数减少,求减少后要凑钱数最少所需硬币数,属于原问题的子结构,已求出解
3.在上述求出的结果集中,选择最小值,即为要凑够该钱数所需的最少硬币数
由此可以看出,每个问题的最优值都是借其子结构的最优值得到的。
而该算法的最小的子结构的最优解是已知的,即:当要凑钱数为0元时,最少需要0枚硬币。
利用这个最小的子结构,通过递推式便可求出所指定值凑够钱数的最优值
上面所提到的递推式,便是状态转移方程。利用已知状态,不断通过状态转移方程求解,便得到了最优值和最优解。
下面看一下硬币组合问题的数学描述:
d(i)=min{ d(i-vj)+1 },其中i-vj >=0,vj表示第j个硬币的面值,i表示要凑够i元,d(i)表示凑够i元最少需要的硬币数。即:
0 i == 0 时
min_coin_num(i) = {
min{ min_coin_num( i-coin_value(j) )+1 | i-coin_value(j)>0} coin_value(j)表示第j种硬币的面值 i > 0 时
当总值total_value为i时, 对于所有的 coin_value(j) < i的硬币j ,取min{ min_coin_num(i-coin_value(j)) }
最后,该算法的python实现:
1 # 如果我们有面值为1元、3元和5元的硬币若干枚,如何用最少的硬币凑够11元
2 __author__ = 'ice'
3
4
5 def select_coin(coin_value, total_value):
6 min_coin_num = [0]
7 for i in range(1, total_value + 1):
8 min_coin_num.append(float('inf'))
9 for value in coin_value:
10 if value <= i and min_coin_num[i - value] + 1 < min_coin_num[i]:
11 min_coin_num[i] = min_coin_num[i - value] + 1
12
13 return min_coin_num
14
15
16 result = select_coin([1, 3, 5], 11)
17 print("coin number:" + str(result[-1]))
- [编程经验]Python生成器、迭代器与yield语句小结
- TensorFlow从0到1 - 12 - TensorFlow构建3层NN玩转MNIST
- 数据城堡参赛代码实战篇(四)---使用pandas合并数据表
- HDU 2586 How far away ?
- HDU 3078 Network
- 数据城堡参赛代码实战篇(三)---我们来探究一个深奥的问题!
- 数据城堡参赛代码实战篇(二)---使用pandas进行数据去重
- 洛谷P3375 【模板】KMP字符串匹配
- Day5下午解题报告1
- [编程经验] Python中处理时间的方法小结
- 数据城堡参赛代码实战篇(一)---手把手教你使用pandas
- [编程经验] SciPy之图像处理小结
- Day6上午解题
- TensorFlow从0到1 - 5 - TensorFlow轻松搞定线性回归
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- GoldenEye靶机渗透
- RBash - 受限的Bash绕过
- 【Rust日报】2020-08-10:在 Rust 中存储连续数据
- 用 Docker 打包 Node.js 程序
- 再见! JSP !
- SocketFTP多线程处理
- 面经手册 · 第4篇《HashMap数据插入、查找、删除、遍历,源码分析》
- Xray配合awvs漏洞扫描
- 无聊的MISC题解
- MISC-convert | 旋转跳跃
- MISC-多彩
- Web-高明的黑客
- CVE-2017-7529 Nginx整数溢出漏洞分析
- 【Vulnhub】pegasus
- 2019-11-20-app专项测试【Android电量】Battery Historian耗电分析