[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的思想,相当于我们可以不断找到更新 ...
随机推荐
- JavaFX Metro UI 和 开发库
目录 [隐藏] 1 Metro UI For JavaFX! 1.1 例子 1.2 Switch 1.3 Button 1.4 案例: 2 ConsrolsFX 3 Notification 的使用 ...
- 一文读懂JS中的原型和原型链(图解)
讲原型的时候,我们应该先要记住以下几个要点,这几个要点是理解原型的关键: 1.所有的引用类型(数组.函数.对象)可以自由扩展属性(除null以外). 2.所有的引用类型都有一个’_ _ proto_ ...
- 从零开发一款自己的小程序UI组件库(二)
写在前面:从零开发一款自己的小程序UI组件库(一) 上节我们讲到初始化组件库模板.模板文件概述.模板上传npm以及npm包文件下载至本地并运用到项目.这节我们继续,内容主要有基础UI组件库的搭建(bu ...
- 爬虫之爬取电影天堂(request)
#需要通过代码打开https://www.dytt8.net/网站,拿到网站内容 from urllib.request import urlopen #拿到urlopen import re con ...
- JVM调优前戏之JDK命令行工具---jhat
在JDK的命令行中,一般开发人员最耳熟能详的肯定就是java,javac,javap等常用命令,不过在jdk/bin下还有许多其他的命令行工具,它们被用来监测JVM运行时的状态,下面我们来详细解读一下 ...
- DFS-深度优先算法解决迷宫问题
/*main.cpp*/#define _CRT_SECURE_NO_WARNINGS #include<iostream> using namespace std; int sr, sc ...
- Kafka 系列(四)—— Kafka 消费者详解
一.消费者和消费者群组 在 Kafka 中,消费者通常是消费者群组的一部分,多个消费者群组共同读取同一个主题时,彼此之间互不影响.Kafka 之所以要引入消费者群组这个概念是因为 Kafka 消费者经 ...
- 由group by引发的sql_mode的学习
前言 在一次使用group by查询数据库时,遇到了问题.下面先搭建环境,然后让问题复现,最后分析问题. 一 问题复现 mysql版本 建表插入数据 表的结构 现在问题来了:我想查询上面表中每个部门年 ...
- Spring Boot 与 Mybatis、Mysql整合使用的例子
第一步: 创建一个SpringBoot的工程,在其中的Maven依赖配置中添加对JDBC.MyBatis.Mysql Driver的依赖具体如下: <!-- JDBC --> <de ...
- vue入门:用户管理demo
该demo纯前端实现 使用到vue技术点: 1.在该demo中使用到的vue指令:{{}}. v-if. v-model. @click v-for 2.在该demo中使用到的事件修饰符: .prev ...