题目三

给定数组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. 10分钟了解一致性hash算法

    应用场景 当我们的数据表超过500万条或更多时,我们就会考虑到采用分库分表:当我们的系统使用了一台缓存服务器还是不能满足的时候,我们会使用多台缓存服务器,那我们如何去访问背后的库表或缓存服务器呢,我们 ...

  2. Mysql无法启动情况下,如何恢复数据?

    本文适用于,mysql无法启动,但数据文件未丢失的情况. Mysql因意外情况,导致无法启动,数据库未做备份的情况下,如何将数据迁移至其他数据库中. 原数据库地址:192.168.1.100(以下简称 ...

  3. Linux下,为应用程序添加桌面图标(ubuntu18.4)

    一.桌面图标位置 Lniux下桌面图标储存路径为:/usr/share/applications 二.桌面图标格式 所有桌面图标格式均为desktop,即名为XXX.desktop 三.编辑内容(常用 ...

  4. 用HTML5的canvas做一个时钟

    对于H5来说,canvas可以说是它最有特色的一个地方了,有了它之后我们可以随意的在网页上画各种各样的图形,做一些小游戏啊什么的.canvas这个标签的用法,在网上也有特别多的教程了,这里就不作介绍了 ...

  5. spring cloud stream 经验总结

    ---恢复内容开始--- 基本概念 spring: cloud: stream: kafka: binder: brokers: cloudTest:19092 zk-nodes: cloudTest ...

  6. C语言数组排序——冒泡排序、选择排序、插入排序

    一.冒泡排序 原理解析:(以从小到大排序为例)在一排数字中,将第一个与第二个比较大小,如果后面的数比前面的小,则交换他们的位置. 然后比较第二.第三个……直到比较第n-1个和第n个,此时,每一次比较都 ...

  7. 算法与数据结构基础 - 字典树(Trie)

    Trie基础 Trie字典树又叫前缀树(prefix tree),用以较快速地进行单词或前缀查询,Trie节点结构如下: //208. Implement Trie (Prefix Tree)clas ...

  8. 我的C语言学习1

    学习是快乐的,尤其是从之前看到一个程序的一头雾水到大致懂了是怎么回事,这个过程是兴奋开心的,让我不断的前进,不能自拔,今天就要结束,总结一下. 1.1-第一个C语言 #include<stdio ...

  9. 40道经典java多线程面试题

    40道经典java多线程面试题 题目来源 看完了java并发编程的艺术,自认为多线程"大成",然后找了一些面试题,也发现了一些不足. 一下问题来源于网上的博客,答案均为本人个人见解 ...

  10. .NET使用Bogus生成大量随机数据

    .NET如何生成大量随机数据 在演示Demo.数据库脱敏.性能测试中,有时需要生成大量随机数据.Bogus就是.NET中优秀的高性能.合理.支持多语言的随机数据生成库. Bogus的Github链接: ...