[DP]换钱的方法数
题目三
给定数组arr, arr中所有的值都为整数且不重复.每个值代表一种面值的货币,每种面值的货币可以使用任意张,在给定一个整数aim代表要找的钱数,求换钱有多少种方法.
解法一 --暴力递归
用0张arr[0], 让arr[1...N-1]组成剩下的钱,这样的方法数记为res1
用1张arr[0], 让arr[1...N-1]组成剩下的钱(aim - arr[0]),方法数记为res2
用2张arr[0], 让arr[1...N-1]组成剩下的钱(aim - 2 * arr[0]),方法数记为res3
...
从上面可以看出,这是一个递归的过程,总的方法书为res1 + res2 +...
代码一
int process1(vector<int> arr, int index, int aim) {
int res = 0;
if (index == int(arr.size()))
res = aim == 0 ? 1 : 0;
else {
for (int k = 0; k * arr[index] <= aim; k ++) //k代表使用arr[index]的张数
res += process1(arr, index + 1, aim - k * arr[index]);
}
return res;
}
// 暴力递归的方法
int coins1(vector<int> arr, int aim) {
if (arr.size() == 0 || aim < 0)
return 0;
return process1(arr, 0, aim);
}
方法二--记忆化搜索
递归多次调用函数自己,会消耗大量的栈空间.递归的一个初级的优化方法就是用一张表记忆已经计算出的结果,避免重复计算.这张表的维度需要分析递归过程中那些是变量.从上面的代码中可以看到arr是不变的,只有index和aim在递归中是变化的,可以做出相应维度的map表来表示递归的过程.代码如下:
代码
int process2(vector<int> arr, int index, vector<vector<int> > &mapTable, int aim) {
int res = 0;
if (index == int(arr.size()))
res = aim == 0? 1:0;
else {
//mapTable 中的特殊值,0说明mapTable[i][j]没有被计算过,-1表示计算过,但是返回值为0
int mapValue = 0;
for (int k = 0; k * arr[index] <= aim; k ++) {
mapValue = mapTable[index + 1][aim - k * arr[index]];
if (mapValue != 0)
res += mapValue == -1 ? 0 : mapValue;
else
res += process2(arr, index + 1, mapTable, aim - arr[index] * k);
}
mapTable[index][aim] = res == 0 ? -1 : res;
}
return res;
}
//记忆化搜索方法
int coins2(vector<int> arr, int aim) {
if (arr.size() == 0 || aim < 0)
return 0;
//记忆搜索表,用于保存递归的中间结果,避免重复计算
vector<vector<int> > mapTable(arr.size() + 1, vector<int>(aim + 1));
return process2(arr, 0, mapTable, aim);
}
方法三--动态规划
可以直接参考代码,时间复杂度为O(N * aim^2)
代码
// 动态规划的方法
int coins3(vector<int> arr, int aim) {
int length = arr.size();
if (length == 0 || aim < 0)
return 0;
vector<vector<int> > dp(length, vector<int>(aim + 1));
for (int k = 0; k * arr[0] <= aim ; k ++) // 初始化第一行
dp[0][k * arr[0]] = 1;
for (int i = 1; i < length; i ++) // 初始化第一列,0张a[i]可以构成0元钱也是一种方法
dp[i][0] = 1;
int sum;
for (int i = 1; i < length; i ++) {
for (int j = 1; j <= aim; j ++) {
sum = 0;
for (int k = 0; k * arr[i] <= j; k++)
sum += dp[i - 1][j - k * arr[i]];
dp[i][j] = sum;
}
}
return dp[length - 1][aim];
}
动态规划--优化
一
上面的DP代码最内层的for循环是根据递归中的步骤来表达的.步骤一的方法数为dp[i-1][j],而第二种到第k种情况的方法数累加值其实就是dp[i][j-arr[i]]的值.所以递推方程为:
dp[i][j] = dp[i-1][j] + dp[i][j-arr[i]]
这样时间复杂度就简化成了O(N*aim),
二
还有进一步的优化就是空间,上面的动态规划空间复杂度都是O(N*aim),通过把二维的dp数组用一维数组来代替,滚动计算能够是空间复杂度降低到O(aim)
[DP]换钱的方法数的更多相关文章
- 算法进阶面试题07——求子数组的最大异或和(前缀树)、换钱的方法数(递归改dp最全套路解说)、纸牌博弈、机器人行走问题
主要讲第五课的内容前缀树应用和第六课内容暴力递归改动态规划的最全步骤 第一题 给定一个数组,求子数组的最大异或和. 一个数组的异或和为,数组中所有的数异或起来的结果. 简单的前缀树应用 暴力方法: 先 ...
- [程序员代码面试指南]递归和动态规划-换钱的方法数(DP,完全背包)
题目描述 给定arr,arr中所有的值都为正数且不重复.每个值代表一种面值的货币,每种面值的货币可以使用任意张,再给定一个整数aim,求组成aim的方法数. 解题思路 完全背包 和"求换钱的 ...
- 算法之Python实现 - 003 : 换钱的方法数
[题目]给定数组arr,arr中所有的值都为正数且不重复.每个值代表一种面值的货币,每种面值的货币可以使用任意张,再给定一个整数aim代表要找的钱数,求组成aim的方法数. [代码1]递归 impor ...
- 2017 Wuhan University Programming Contest (Online Round) B Color 树形dp求染色方法数
/** 题目:Color 链接:https://oj.ejq.me/problem/23 题意:给定一颗树,将树上的点最多染成m种颜色,有些节点不可以染成某些颜色.相邻节点颜色不同.求染色方法数. 思 ...
- [程序员代码面试指南]递归和动态规划-机器人达到指定位置方法数(一维DP待做)(DP)
题目描述 一行N个位置1到N,机器人初始位置M,机器人可以往左/右走(只能在位置范围内),规定机器人必须走K步,最终到位置P.输入这四个参数,输出机器人可以走的方法数. 解题思路 DP 方法一:时间复 ...
- 数字和为sum的方法数
[编程题] 数字和为sum的方法数 给定一个有n个正整数的数组A和一个整数sum,求选择数组A中部分数字和为sum的方案数. 当两种选取方案有一个数字的下标不一样,我们就认为是不同的组成方案. 输入描 ...
- 一个n*n 的方格,要从左上角走到右下角,一次只能往右或往下走一步,求算法得出所有走动的方法数。
题目一:一个n*n 的方格,要从左上角走到右下角,一次只能往右或往下走一步,求算法得出所有走动的方法数. 分析:对于第(i,j)个格子,只有向右走一步到达或者向左走一步到达,dp(i,j) = d(i ...
- [Android] Android统计Apk , jar包方法数
reference to : http://www.jianshu.com/p/61e8f803e0d1 Android在开发过程中,随着引用的库以及业务的增多,不可避免的会出现64K limit问题 ...
- POJ 3463 有向图求次短路的长度及其方法数
题目大意: 希望求出走出最短路的方法总数,如果次短路只比最短路小1,那也是可取的 输出总的方法数 这里n个点,每个点有最短和次短两种长度 这里采取的是dijkstra的思想,相当于我们可以不断找到更新 ...
随机推荐
- kali,ubuntu, debain DNS 配置
kali 是基于 debain 的一个 Linux 发行版 DNS 的配置 是在文件 /etc/resolv.conf 下. 但是,我们会发现 /etc/resolv.conf 每次重启都会失效, ...
- Paxos算法原理
1.从ACID到CAP 我们知道传统集中式系统中实现ACID是很简单的,在分布式环境中,涉及到不同的节点,节点内的ACID可以控制,那么节点间的ACID如何控制呢?构建一个可用性和一致性的分布系统成为 ...
- java并发编程(二十三)----(JUC集合)ConcurrentSkipListMap介绍
ConcurrentSkipListMap提供了一种线程安全的并发访问的排序映射表.内部是SkipList(跳表)结构实现,在理论上能够在O(log(n))时间内完成查找.插入.删除操作. 理解Ski ...
- HTML发展历程
HTML是超文本标记语言的缩写,不同于C或JAVA等编程语言,HTML由标签组成.通过标签可以在网页中插入文字.图片.链接.音频.视频等元素,进而描述网页.和Windows一样,随着技术的发展,HTM ...
- 一文了解java异常机制
1.异常的概述 1.1什么是异常? 异常:程序在运行过程中发生由于外部问题导致的程序异常事件,发生的异常会中断程序的运行.(在Java等面向对象的编程语言中)异常本身是一个对象,产生异常就是产生了一个 ...
- Yii GridView Ajax 刷新
Yii GridView Ajax 刷新,当页面点击一个按钮时,刷新数据. 1.控制器 <?php class privController extends Controller{ publi ...
- 朴素贝叶斯python代码实现(西瓜书)
朴素贝叶斯python代码实现(西瓜书) 摘要: 朴素贝叶斯也是机器学习中一种非常常见的分类方法,对于二分类问题,并且数据集特征为离散型属性的时候, 使用起来非常的方便.原理简单,训练效率高,拟合效果 ...
- day0203
day02 1.for i in range() --->用于设置for循环的迭代设置. ranage 也是一个前闭后开的. 2.random.randrange() --->随机产生给予 ...
- 使用coding和hexo快速搭建博客
欢迎访问我的个人博客皮皮猪:http://www.zhsh666.xyz 今天教大家怎么用hexo快速搭建自己的博客.我不是专业人士,不懂前端知识,所以我十分讨厌那些专业术语,讲了一大堆,对于技术小白 ...
- 2013-2014 ACM-ICPC Pacific Northwest Regional Contest L.Languages
题意略. 思路: 这种字符串的模拟题,应该熟练使用stringstream. 详见代码: #include<bits/stdc++.h> using namespace std; map& ...