题目三

给定数组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]换钱的方法数的更多相关文章

  1. 算法进阶面试题07——求子数组的最大异或和(前缀树)、换钱的方法数(递归改dp最全套路解说)、纸牌博弈、机器人行走问题

    主要讲第五课的内容前缀树应用和第六课内容暴力递归改动态规划的最全步骤 第一题 给定一个数组,求子数组的最大异或和. 一个数组的异或和为,数组中所有的数异或起来的结果. 简单的前缀树应用 暴力方法: 先 ...

  2. [程序员代码面试指南]递归和动态规划-换钱的方法数(DP,完全背包)

    题目描述 给定arr,arr中所有的值都为正数且不重复.每个值代表一种面值的货币,每种面值的货币可以使用任意张,再给定一个整数aim,求组成aim的方法数. 解题思路 完全背包 和"求换钱的 ...

  3. 算法之Python实现 - 003 : 换钱的方法数

    [题目]给定数组arr,arr中所有的值都为正数且不重复.每个值代表一种面值的货币,每种面值的货币可以使用任意张,再给定一个整数aim代表要找的钱数,求组成aim的方法数. [代码1]递归 impor ...

  4. 2017 Wuhan University Programming Contest (Online Round) B Color 树形dp求染色方法数

    /** 题目:Color 链接:https://oj.ejq.me/problem/23 题意:给定一颗树,将树上的点最多染成m种颜色,有些节点不可以染成某些颜色.相邻节点颜色不同.求染色方法数. 思 ...

  5. [程序员代码面试指南]递归和动态规划-机器人达到指定位置方法数(一维DP待做)(DP)

    题目描述 一行N个位置1到N,机器人初始位置M,机器人可以往左/右走(只能在位置范围内),规定机器人必须走K步,最终到位置P.输入这四个参数,输出机器人可以走的方法数. 解题思路 DP 方法一:时间复 ...

  6. 数字和为sum的方法数

    [编程题] 数字和为sum的方法数 给定一个有n个正整数的数组A和一个整数sum,求选择数组A中部分数字和为sum的方案数. 当两种选取方案有一个数字的下标不一样,我们就认为是不同的组成方案. 输入描 ...

  7. 一个n*n 的方格,要从左上角走到右下角,一次只能往右或往下走一步,求算法得出所有走动的方法数。

    题目一:一个n*n 的方格,要从左上角走到右下角,一次只能往右或往下走一步,求算法得出所有走动的方法数. 分析:对于第(i,j)个格子,只有向右走一步到达或者向左走一步到达,dp(i,j) = d(i ...

  8. [Android] Android统计Apk , jar包方法数

    reference to : http://www.jianshu.com/p/61e8f803e0d1 Android在开发过程中,随着引用的库以及业务的增多,不可避免的会出现64K limit问题 ...

  9. POJ 3463 有向图求次短路的长度及其方法数

    题目大意: 希望求出走出最短路的方法总数,如果次短路只比最短路小1,那也是可取的 输出总的方法数 这里n个点,每个点有最短和次短两种长度 这里采取的是dijkstra的思想,相当于我们可以不断找到更新 ...

随机推荐

  1. Docker最简单入门之(二)——简单使用Docker

    0.前言 本章主要写一些怎么使用Docker,拉取镜像和创建容器等之类的Docker的常用基本操作.在开始写之前,大家需要明白一下几个名词的含义 1.镜像:镜像是指一个类似于安装包的东西,尝试安装过电 ...

  2. 学习Qt的一点小感想

    作为一名电子信息工程的学生,嵌入式似乎是不二的选择,然后我便学习了一下在嵌入式广泛应用的QT软件,刚开始就是学学控件,觉得还是简单,也觉得比较新颖,可是到了做一些具体的小东西就会发现学的东西远远不够, ...

  3. JAVA jobs

    Java岗位1, SpringMVC, spring, mybaits2, 高并发编程3, mysql或者oracle SQL调优及函数,存储过程,JOB调度

  4. SpringBoot第二天

    一,SpringBoot 整合 jsp 技术 1,创建项目 2,修改 pom 文件,添加坐标 <project xmlns="http://maven.apache.org/POM/4 ...

  5. 关于 IntelliJ 的 IDEA PyCharm 等更新 2019.2 后中文乱码 的解决方案

    关于IntelliJ 的2019.2 更新后的中文乱码解决方案 设置 备用字体 file -> Setting -> Editor ->Font 由于编程常用英文首选字体font默认 ...

  6. Salesforce LWC学习(四) 父子component交互 / component声明周期管理 / 事件处理

    我们在上篇介绍了 @track / @api的区别.在父子 component中,针对api类型的变量,如果声明以后就只允许在parent修改,son component修改便会导致报错. sonIt ...

  7. 解决Springboot整合ActiveMQ发送和接收topic消息的问题

    环境搭建 1.创建maven项目(jar) 2.pom.xml添加依赖 <parent> <groupId>org.springframework.boot</group ...

  8. opencv 视觉项目学习笔记(二): 基于 svm 和 knn 车牌识别

    车牌识别的属于常见的 模式识别 ,其基本流程为下面三个步骤: 1) 分割: 检测并检测图像中感兴趣区域: 2)特征提取: 对字符图像集中的每个部分进行提取: 3)分类: 判断图像快是不是车牌或者 每个 ...

  9. ATX agent+UIautomation2 自动化测试介绍

    纯搬运贴,内容几乎来源于作者的几篇介绍文章,这里做了整合 目前ATX+UIautomator2 处于自动化界的浪口风尖,现在有幸终于有时间对ATX进行了粗浅的了解 为什么要用ATX ATX+UIaut ...

  10. 如何在logback.xml中自定义动态属性

    当使用logback来记录Web应用的日志时,我们通过在logback.xml中配置appender来指定日志输出格式及输出文件路径,这在一台主机或一个文件系统上部署单个实例没有问题,但是如果部署多个 ...