题目:换钱的方法数

给定数组 arr, arr中所有的值都为正数且不重复。每个值代表一种面值的货币,每种面值的货币可以使用任意张,再给定一个整数aim代表要找的钱数,求换钱有多少种方法

将原文的伪代码进行C++实现

程序员代码面试指南第四章递归和动态规划  点击打开链接

例1:arr = [5, 10, 25, 1] ,aim = 15, 6种方法

1) 3张5元; 2)1张10元+1张5元; 3)1张10元+5张1元; 4)10张1元+1张5元;5)2张5元+5张1元; 6)15张1元

注:暴力递归和记忆化搜索方法,见前文 动态规划C++实现--换钱的方法数(一)(暴力递归 和 记忆化搜索)

总结:

  • 暴力递归                   时间复杂度 O(aim^N)
  • 记忆化搜索               时间复杂度 O (N*aim^2)
  • 动态规划                   时间复杂度 O (N*aim^2)
  • 改进动态规划            时间复杂度 O (N*aim)      额外空间复杂度 O (N*aim)
  • 压缩空间的动态规划   时间复杂度 O (N*aim)     额外空间复杂度 O (aim)

3、动态规划

动态规划方法,生成函数为 N、列数为 aim+1的二维矩阵dp。(时间复杂度O(N*aim^2))

dp[i][j] 表示在使用 arr[0,1,..,i]货币的情况下,组成钱数为 j 的总方法数。

第一步:边界初始化

dp[..][0] 表示钱数为 0, 不使用任何货币,所以第一列统一设定为1.

dp[0][..] 表示只使用 arr[0] 的情况下,组成的钱的方法数,所以在 dp[0][arr[0]*k] = 1  (0 <= arr[0] * k <= aim).

第二步:中间项的更新,即求dp[i][j]

举个例子说明更新规则:

(1) 用 0 张 arr[i] 货币,只使用 arr[0,..,i-1]货币,方法数为dp[i-1][j].

(1) 用 1 张 arr[i] 货币,只使用 arr[0,..,i-1]货币,方法数为dp[i-1][j-arr[i]].

.......

(1) 用 k 张 arr[i] 货币,只使用 arr[0,..,i-1]货币,方法数为dp[i-1][j - k*arr[i]].   (j - k*arr[i] >=0)

dp[i][j] = dp[i-1][j-1] + dp[i-1][j-arr[i]] + ....... + dp[i-1][j - k*arr[i]]

最后dp[N-1][aim即为方法总数。

C++源码如下:

// 换钱的方法数 <动态规划> <复杂度0(N*aim^2)>
// 空间换时间,按顺序进行输出
#include<bits/stdc++.h>
using namespace std;
int coins3(int arr[], int aim);int main(){int N, aim; cin >> N >> aim;int arr[N];for (int i = 0; i < N; i++){cin >> arr[i];}cout << coins3(arr, aim) <<endl;return 0;
}int coins3(int arr[], int aim) {int dp[sizeof(arr)][aim+1];memset(dp,0, sizeof(dp));for (int i = 0; i < sizeof(arr); i++){dp[i][0] = 1;}for (int j = 1; j * arr[0] <= aim; j++){dp[0][j*arr[0]] = 1;}int num;for (int i = 1; i < sizeof(arr); i++) {for (int j = 1; j <= aim; j++) {num = 0;for (int k = 0; j - k * arr[i] >= 0; k++) {num += dp[i - 1][j - k * arr[i]];}dp[i][j] = num;}}return dp[sizeof(arr) - 1][aim];
}

输入:

输出:

4、动态规划(改进时间复杂度O(N*aim))

动态规划方法中的第二步中进行动态更新的过程如下,思考是否存在重复计算的部分?

dp[i][j] = dp[i-1][j-1] + dp[i-1][j-arr[i]] + ....... + dp[i-1][j - k*arr[i]]

观察下面递推步骤:

dp[i][j-arr[i]] = dp[i-1][j-arr[i]] + dp[i-1][j- 2*arr[i]] + ....... + dp[i-1][j - k*arr[i]]

可以很容易得到动态规划的红色部分是等价的。那么就可以进行替换。

得到新的更新步骤:

dp[i][j] = dp[i-1][j-1] + dp[i][j -arr[i]]

C++源码如下:

// 换钱的方法数 <动态规划> <复杂度0(N*aim)>
// 空间换时间,按顺序进行输出
// dp[i][j] = dp[i-1][j] + dp[i][j-arr[i]];
#include<bits/stdc++.h>
using namespace std;
int coins4(int arr[], int aim);int main(){int N, aim; cin >> N >> aim;int arr[N];for (int i = 0; i < N; i++) {cin >> arr[i];}cout << coins4(arr, aim) <<endl;return 0;
}int coins4(int arr[], int aim){int dp[sizeof(arr)][aim + 1];memset(dp, 0, sizeof(dp));for (int i = 0; i < sizeof(arr); i++){dp[i][0] = 1;}for (int j = 1; j * arr[0] <= aim; j++){dp[0][j * arr[0]] = 1;}for (int i = 1; i < sizeof(arr); i++) {for (int j = 1; j <= aim; j++){dp[i][j] = dp[i - 1][j];dp[i][j] += (j - arr[i]) >= 0 ? dp[i][j - arr[i]] : 0;//      cout << i <<","<< j <<": "<< dp[i][j]<<endl;}}return dp[sizeof(arr) - 1][aim];
}

5、动态规划(空间压缩)

时间复杂度O(N*aim),额外空间复杂度O(aim)

对于动态规划改进方法的二维矩阵进行改进为一维矩阵,减小空间。

C++代码如下

// 换钱的方法数 <动态规划> <复杂度0(N*aim)>
// 额外空间复杂度 o(aim)
#include<bits/stdc++.h>
using namespace std;
int coins5(int arr[], int aim);int main(){int N, aim; cin >> N >> aim;int arr[N];for (int i = 0; i < N; i++) {cin >> arr[i];}cout << coins5(arr, aim) <<endl;return 0;
}int coins5(int arr[], int aim){int dp[aim + 1];memset(dp, 0, sizeof(dp));for (int j = 0; j * arr[0] <= aim; j++){dp[j * arr[0]] = 1;}for (int i = 1; i < sizeof(arr); i++) {for (int j = 1; j <= aim; j++){dp[j] += j - arr[i] >= 0 ? dp[j - arr[i]] : 0;//      cout << i <<","<< j <<": "<< dp[j]<<endl;}}return dp[aim];
}

以上代码没有考虑输出不符合规范的问题。如有要求,需要对于输入进行限制。

动态规划C++实现--换钱的方法数(二)(动态规划及其改进方法)相关推荐

  1. 算法53----换钱的最小次数和方法数【动态规划】

    一.题目:换钱的最小次数 给定数组arr,arr中所有的值都为正数且不重复.每个值代表一种面值的货币,每种面值的货币可以使用任意张,再给定一个整数aim代表要找的钱数,求组成aim的最少货币数. 举个 ...

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

    第一题 给定一个数组,求子数组的最大异或和. 一个数组的异或和为,数组中所有的数异或起来的结果. 简单的前缀树应用 暴力方法: 先计算必须以i结尾的子数组的异或和,然后再计算机i+1的,以此类推... ...

  3. 【算法-Java实现】 换钱的方法数(暴力递归法)

    [算法-Java实现] 换钱的方法数(暴力递归法) 文章目录 [算法-Java实现] 换钱的方法数(暴力递归法) 一.问题描述: 二.问题解答: **举例:** **思路:==暴力递归==** 三.算 ...

  4. 动态规划问题——换钱的方法数

    题目: 给定数组arr,arr中所有的值都为正数且不重复.每个值代表一种面值的货币,每种面值的货币可以使用任意张,再给定一个整数aim,代表要找的钱数,求换钱有多少种方法. 举例: arr = [5, ...

  5. 递归与动态规划---换钱的方法数

    [问题] 给定数组arr,arr中所有的值都为整数且不重复.每个值代表一种面值的货币,每种货币有无数张,再给定一个整数aim代表要找的钱数,求换钱的方法有多少种. [基本思路] 这道题的经典之处在于它 ...

  6. android解决方法数超过65536问题,(满则溢)Android AS打包提示方法数超65536的解决方法...

    满则溢 平常大家喝饮料啥的都知道一个杯子的容量,如果超出就溢出,这个时候只能在加个杯子装了,Android开发也是如此,当方法数超过65536的时候就需要分包 为啥出现 项目当中添加的依赖和架包太多了 ...

  7. 【动态规划】象棋里马走到指定位置的方法数(java)

    动态规划--马走到指定位置的方法数 马走到指定位置的方法数 暴力递归 代码演示 动态规划 代码演示 动态规划专题 马走到指定位置的方法数 想象一个象棋的棋盘, 然后把整个棋盘放入第一象限,棋盘的最左下 ...

  8. 动态规划:得到目标货币的方法数(有限张货币 + 面值相同的货币相同)

    1.题目 arr 是货币数组,其中的值都是正数. 再给定一个正数 aim. 每个值都认为是一张货币,认为值相同的货币没有任何不同,返回组成 aim 的方法数. 例如:arr = {1,2,1,1,2, ...

  9. Android 使用android-support-multidex解决Dex超出方法数的限制问题,让你的应用不再爆棚

    时之沙: http://blog.csdn.net/t12x3456 随着应用不断迭代,业务线的扩展,应用越来越大(比如集成了各种第三方sdk或者公共支持的jar包,项目耦合性高,重复作用的类越来越多 ...

最新文章

  1. 服务器虚拟化组网方案,服务器虚拟化部署方案计划.doc
  2. 混合云扛起云存储领军大旗
  3. linux恢复出厂设置_怎么恢复tp-link路由器出厂设置 恢复tp-link出厂设置方法【详解】...
  4. 汇编小程序---计算十以内两个数的相加
  5. 就是这么流弊!三行Python代码,让数据处理速度提高2到6倍
  6. 《犯罪心理学》读书笔记(part9)--犯罪心理的主观差异(下)
  7. Exchange管理之:使用Telnet发送邮件
  8. 软件工程学习进度第八周暨暑期学习进度之第八周汇总
  9. 0基础学python要多久-零基础学习Python开发需要多长时间?
  10. Netty NioEventLoop 启动过程源码分析
  11. CAD2008详细安装教程和激活失败方法
  12. AD15实际工程的基本操作
  13. 网课搜题公众号接口怎么对接?最新接口-麦麦题
  14. Java8 Instant 时间戳
  15. ajax如何传递josn数据,jq之ajax以及json数据传递
  16. 快来和网红 ChatGPT 聊天!!
  17. 163邮箱会员揭秘,163邮箱注册,你最想了解的几件事
  18. 企业承担社会责任的必要性
  19. win7开启uasp协议_移植win8通用USB驱动到win7上并开启UASP功能!
  20. C语言实现SM3(基于GMSSL)

热门文章

  1. 第三月模拟题——炉石传说
  2. Unity调用大华相机SDK采集图像及基本功能设定
  3. hibernate: 用Disjunction和Conjunction构造复杂的查询条件
  4. 利用 eutils 实现自动下载序列文件(python实现)
  5. 想成为魅力十足的人的十大习惯
  6. PSAM卡之常用APDU指令错误码
  7. 双重福利:计算机图书满100减50+满99 减10叠加券,更有抽奖送书活动,点击查看!...
  8. OpenGL + Win32 SDK 开发框架的搭建(C语言版)
  9. 易共享android工具下载,EasyShare app
  10. mysql数据库asc_mysql数据库