动态规划(三)——最少硬币和所有硬币问题
硬币问题
- 一、最少硬币问题
- 二、打印最少硬币组合
- 三、所有硬币组合
- 3.1硬币数量不限制
- 3.2硬币数量限制
一、最少硬币问题
有n种硬币,面值为v1…vn,数量无限,选用硬币,使其和金额为s,要求求出最少的硬币组合。
首先我们应该有打表的思想,将任意金额的最少硬币组合数量存到一个数组里,输入一个金额时就可以直接查询数组中对应的硬币最少数量。
定义一个int Min[MONEY],Min[i]是金额i对应的最少硬币数量。Min[i]这样记录子问题最优解的数据称为“状态”。
讲解以5种面值(1、5、10、25、50)的硬币为例:
第一步:只使用1分硬币。
初始值Min[0] = 0,表示0个硬币可以表示0金额,其他的Min[i]为无穷大。
那Min[1]如何计算?
这时可以在Min[0]的基础上加一个1分硬币,此时Min[1] = Min[0] + 1 = Min[1] = Min[1-1] +1 。
此时可以推导出第一个递推公式:
Min[i] = min(Min[i],Min[i-1]+1)
故只用1分硬币的组合结果如下:
第二步:在使用1分硬币的基础上增加第二大面值的5分硬币
此时要在第一步状态的基础上从金额为5开始转换(若金额小于5时,不能用5分硬币替换)
i = 5时,相当于在i = 0的基础上加一个5分硬币,得到Min[5] = Min[5-5] +1 = 1。
1分硬币+5分硬币组合的结果如下:
第一步i=5时,只用1分硬币状态的结果Min[5] = 5,此时根据推导公式
Min[i] = min(Min[i],Min[i-5]+1)
可知,Min[5-5]+1=1比 Min[5]=5更小,故Min[5]=1。第三步:继续处理其他面值的硬币。
接下来就是在第二步的基础上将面值为10的硬币转换。
例如i= 10时,相当于在i = 0的基础上加了一个10分硬币,此时Min[10] = Min[10-10] + 1 = 1。
又第二步中Min[10] = 2,
故Min[10] = min(Min[10] ,Min[10-10]+1) = Min[10-10]+1 = 1。
所以找出规律,所有面值的递推公式都是:
Min[i] = min(Min[i],Min[i-面值]+1)
代码如下:
#include<bits/stdc++.h>
#define fio ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
using namespace std;const int money = 200; //定义最大金额
const int num = 5; //五个硬币
int type[num] = {1,5,10,25,50}; //五种面值
int Min[money];void solve() {for (int i = 1; i < money; i++)Min[i] = INT_MAX;//初始值为无穷大Min[0] = 0;for (int i = 0; i < 5; i++) {for (int j = type[i]; j <= money; j++) {Min[j] = min(Min[j], Min[j-type[i]]+1);}}
}
int main() {fiosolve();int s;while (cin >> s) {cout << Min[s] << endl;}
}
二、打印最少硬币组合
若要打印最少硬币的组合方案,就存一个记录金额 i 所需要的最后一种面值的数组Min_Path[],用这个数组倒推即可得到硬币组合方案。
查询 i 的最优组合:
- 查询Min_Path[i],并得到结果k1
- i-k1=a1,并得到Min_Path[a1] = k2
- a1-k2 = a2,继续计算Min_Path[a2]。
重复计算坐标与对应Min_Path[]数组的差值,并计算Min_Path[差值]的结果,直到计算结果为0。
得出的k1…kj即为结果
如图,计算出Min[]和Min_Path[]:
实例解释,当i=7时:
①Min_Path[7] = 5,说明金额为7的组合中最后一张需要的面值为5。
②Min_Path[7-5]=Min_Path[2] = 1,表示接下来需要的最后一张面值为1。
③Min_Path[2-1]=Min_Path[1]=1,还有一张面值为1。
故i=7时,组合为5分硬币+1分硬币+1分硬币。
#include<bits/stdc++.h>
#define fio ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
using namespace std;const int money = 200; //定义最大金额
const int num = 5; //五个硬币
int type[num] = {1,5,10,25,50}; //五种面值
int Min[money];
int Min_Path[money];
void solve() {for (int i = 0; i < money; i++)Min[i] = INT_MAX; Min[0] = 0;for (int i = 0; i < num; i++) {for (int j = type[i]; j < money; j++) {if (Min[j] > (Min[j-type[i]]+1)) {Min[j] = Min[j-type[i]]+1;Min_Path[j] = type[i];}}}
}
void print(int *Min_Path, int s) {while (s) {printf("%d ", Min_Path[s]);s -= Min_Path[s];}
}
int main() {fioint s;solve();while (cin >> s) {cout << Min[s];print(Min_Path, s);}
}
三、所有硬币组合
3.1硬币数量不限制
有n种硬币,面值为v1,v2……vn,数量无限。输入非负整数s,选用硬币,使其和为s,输出所有可能的硬币组合数量。
定义一个记录状态的数组int dp[],dp[i]表示金额i所对应的组合方案数。需要找到dp[i]和dp[i-1]的递推关系。
同样是用1,5,10,25,50五种面值的硬币:
- 第一步:只用1分硬币。
dp[0] = 1设为初始值,其余为0。
dp[1] = dp[1]+dp[0] = 0+1。
对于其他dp[i]也有dp[i] = dp[i]+dp[i-1],这称为状态转移方程。 - 第二步:加上5分硬币。
i>=5时,dp[i] = dp[i]+dp[i-5] - 第三步:继续处理其他面值硬币的情况。
同理有dp[i] = dp[i]+dp[i-10],dp[i] = dp[i]+dp[i-25],dp[i] = dp[i]+dp[i-50]
#include<bits/stdc++.h>
#define fio ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
using namespace std;const int money = 200; //定义最大金额
const int num = 5; //五个硬币
int type[num] = {1,5,10,25,50}; //五种面值
int dp[money];void solve() {dp[0] = 1;for (int i = 0; i < num; i++) {for (int j = type[i]; j < money; j++) {dp[j] = dp[j]+dp[j-type[i]];}}
}
int main() {fioint s;solve();while (cin >> s) {cout << dp[s] << endl;}
}
3.2硬币数量限制
HDOJ2069-硬币找零
问题描述
假设有5种硬币:50美分,25美分,10美分,5美分和1美分。我们希望以给定的金额使用这些硬币进行更改。
例如,如果我们有11美分,则可以用一枚10美分硬币和一枚1美分硬币,或两枚5美分硬币和一枚1美分硬币,或一枚5美分硬币和六枚1美分硬币进行组合或11个1分硬币。因此,使用上述硬币,有四种方法可以组合11分钱。
编写一个程序,以查找以美分计的任何金额进行组合的不同方式的总数,硬币数量num<=100。
输入项
输入文件包含任意数量的行,每行包含一个数字(≤250),以分币为单位。
输出量
对于每条输入线,输出一条线,其中包含使用上述5种硬币进行更改的不同方式的数量。
样本输入
11
26
样本输出
4
13
这道题要求硬币不能多于100个,因此我们可以建立一个转移矩阵,定义状态为dp[i][j]。其中横向是金额(i<=250),纵向是硬币数(j<=100)。
- 第一步:只用一分硬币。
type[5] = {1,5,10,25,50}
初始化dp[0][0] = 1,其他为0,从dp[0][0]推导后面的状态。
例如dp[1][1]是dp[0][0]进行金额+1、硬币数量+1后的状态转移,状态转移后组合方案数量不变,即dp[0][0] = dp[1][1] = 1。
dp[1][1] = dp[1][1]+dp[0][0]= dp[1][1]+dp[1-1][1-1]
=>
dp[1][1] = dp[1][1]+dp[1-type[0]][1-1]
=> dp[i][j] = dp[i][j]+dp[1-type[0] ][j-1]
对所有dp[i][j]执行如上操作,看图可观察到红色剪头的规律。 - 第二步:加上五分硬币。
当i>=5时,dp[i][j] = dp[i][j]+dp[i-5][j-1]。
dp[i][j] = dp[i][j]+dp[i-type[1]][j-1]。
- 第三步:处理其他面值的硬币
dp[i][j] = dp[i][j] + dp[i-type[k]][j-1],k=2,3,4
矩阵元素dp[i][j]的含义是用j个硬币实现金额i的方案数量。
例如表中dp[6][6]=1,表示用6个硬币凑出6分钱,只有一种方案,即6个1分钱,表中空格即0表示没有方案。
该表中纵坐标相加,就是某金额对应的方案总数。
#include<bits/stdc++.h>
#define fio ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
using namespace std;const int coin = 101;
const int money = 251; //定义最大金额
const int num = 5; //五个硬币
int type[num] = {1,5,10,25,50}; //五种面值
int dp[money][coin];void solve() {dp[0][0] = 1;for (int i = 0; i < num; i++) {for (int j = 1; j < coin; j++) {for (int k = type[i]; k < money; k++) {dp[k][j] += dp[k-type[i]][j-1];}}}
}int main() {fioint ans[money] = {0};int s;solve();for (int i = 0; i < money; i++) for (int j = 0; j < coin; j++)ans[i] += dp[i][j];while (cin >> s) {cout << ans[s] << endl;}
}
动态规划(三)——最少硬币和所有硬币问题相关推荐
- 动态规划:最少硬币数 <-- 最后一步法
[问题描述] 假设有三种硬币,币值分别为2元.5元.7元,且每种硬币都足够多. 若买一种书需要27元,请编程计算最少需要多少枚硬币恰好付清,且不需要找零. [算法分析] 此问题是"最值型&q ...
- python换硬币_Python的硬币兑换动态编程实现记录,CoinChange,最少,组合,python,规划
题目为给定不同面值的n种硬币,面值加起来等于一个特定的数m,求最少需要多少枚硬币实现. 这个问题如果使用暴力求解,需要穷举所有可以加起来等m的组合,时间复杂度为O(m^n). def coin_27_ ...
- Java实现求解硬币问题有1分、2分、5分、10分、50分和100分的硬币各若干枚,现在要用这些硬币支付W元,最少需要多少枚硬币?利用贪心法的思想进行编程
求解硬币问题.有1分.2分.5分.10分.50分和100分的硬币各若干枚,现在要用这些硬币支付W元,最少需要多少枚硬币? 1.我解决该问题编程的思路如下: 首先是利用一个数组A存储硬币面额,再利用另外 ...
- Java黑皮书课后题第8章:**8.11(游戏:九个硬币的正反面)一个3*3的矩阵中放置了9个硬币,这些硬币有些面朝上有朝下。1表示正面0表示反面,每个状态使用一个二进制数表示。使用十进制数表示状态
**8.11(游戏:九个硬币的正反面)一个3*3的矩阵中放置了9个硬币,这些硬币有些面朝上有朝下.1表示正面0表示反面,每个状态使用一个二进制数表示. 题目 题目描述与运行示例 破题:注意对应关系(已 ...
- Python(分治算法)问题 A: 找出伪币_给你一个装有n枚硬币的袋子。n枚硬币中有一个是伪造的,并且那个伪造的硬币比真的硬币要轻一些。你的任务是找出这枚伪造的硬币。
问题 A: 找出伪币 题目描述 给你一个装有n枚硬币的袋子. n枚硬币中有一个是伪造的,并且那个伪造的硬币比真的硬币要轻一些. 你的任务是找出这枚伪造的硬币. 输入 测试数据有多行,第一行是金币的数量 ...
- python 最小硬币数_Python之动态规划(最少硬币数找零)
完整代码: # 动态规划最少硬币数找零 def dpMakeChange(coinValueList, change, minCoins, coinsUsed): for cents in range ...
- 动态规划--用最少的硬币类别找零钱
#include<iostream> using namespace std;//coinNum[i]当前钱为i时需要的最少硬币数 void FindMin(int money,int * ...
- 动态规划求解最少硬币是多少?
多少枚硬币组合问题,最少硬币是多少? 1 确定状态 最后一步(最优策略中使用的最后一枚硬币) 化成子问题(最少的硬币拼出更小面值) 2 转移方程 3 初始条件和边界情况 f[0]=0,如果不能拼出Y, ...
- Python数据结构20:动态规划:找零兑换问题的动态规划解法并显示使用的硬币组合
在我们使用递归算法时,可能会出现规模庞大的重复计算,用一个中间表记录每个计算过的最优解法,就可以避免大量的重复计算.中间结果记录可以很好解决找零兑换问题.实际上,这种方法还不能称为动态规划,而是叫做& ...
最新文章
- 使用TDI与WinSock进行客户端服务器编程
- go 的des加解密
- php正则表达式匹配img中任意属性的方法
- Spring学习(八)AOP详解
- 带你玩转 ui 框架 ——scoped及样式穿透问题详解
- c#语言程序设计上机实验,C#语言程序设计基础实验指导(第3版)
- 划分字母区间c语言,LeetCode(#763):划分字母区间
- Alibaba Druid 源码阅读(四) 数据库连接池中连接获取探索
- UI自动化,元素被遮挡
- 手机屏幕坏了怎么把数据导出来_手机屏幕失灵怎么办 手机屏幕失灵解决办法【详解】...
- Mysql从入门到入魔——3. 查询、排序、WHERE过滤
- 计算机图形学大会和学术刊物编辑
- html制作简单框架网页 实现自己的音乐驿站 操作步骤及源文件下载 (播放功能限mp3文件)...
- 大数据开发跟大数据分析的区别是什么?
- Android使用Google Map服务 - 根据GPS信息在地图上定位
- strcpy函数和strcat函数
- gitlab拉取项目报128 fatal: unable to access ‘xxx.git/‘
- 通过棱镜门看事物的联系
- Deep Learning for Matching in Search and Recommendation 搜索与推荐中的深度学习匹配(1 引言)
- Android Studio 快捷键大全