C++数据结构与算法 动态规划
动态规划:
和贪婪算法一样,动态规划对一个问题的解是一系列的决策过程,贪婪算法依据贪婪准则,做出的每一个抉择都是不可撤回的,在动态规划中,需要考察一系列抉择,已确定一个最优抉择序列是否包含一个最优抉择子序列。
例如,在上图中,要从节点1到达节点5,选择一条最短的路径,第一步可以选择的顶点为2,3,4。假设选择顶点3, 然后决定如何从顶点3到达顶点5。如果选择了一条从顶点3到顶点5一条不是最短的路径,那么即使第一步选择了从顶点1到顶点3这条最短的路径,连起来构成从1到5的路径也不是最短的。
所以在最短路径中,如果第一次选择的是某一个顶点v,那么接下来所选择的从v到d的路径必须是最短的。
2. 背包问题:
在0/1背包问题中,需要选择 的值,如果选择 ,那么背包问题就转换为物品为背包容量认为c的问题,如果选择,则背包问题转换为物品为背包容量认为的问题.,另表示剩余背包的容量。
在第一次选择之后,需要考虑使用剩余的物品满足装在容量为 r 的背包,剩余的物品(2到n)和剩余的容量r规定了在第一次选择之后问题的状态。不管第一次选择的结果如何,必须是这个问题状态的一个最优解。如果不是,那么必有一个更好的选择,假设为,于是便是初始问题的一个更好的解。
当最优抉择包含最优抉择系序列的时候,可以建立动态规划递归方程,可以高效的解决问题。
0/1背包问题:
在背包问题中,最优选择序列由最优选择子序列构成,假设 f(i,y) 表示剩余容量为y, 剩余物品为 i,i+1,...n 的背包问题的最优解的值(0/1)。
剩余第n个物品,如果剩余的容量y大于该物品的体积 ,则 f(n,y) 的返回值为p_n,否则为0;
所以有:
根据最优序列由最优子序列构成的结论,可以得到上面的递推公式。
所以无论第一次的选择是什么,接下来的选择一定是当前状态下的最优选择。称此为最优原则。
动态规划求解的步骤:
1. 证实最有原则是适用的
2. 建立动态规划的递归方程
3. 求解动态规划的递归方程以获得最优解
4. 沿着最优解的生成过程进行回溯
关于编程求解动态规划的问题:
在求解动态规划递归方程的规程中,应该避免递归中的重复计算。可以通过以迭代的方式来避免递归过程中的重复计算。迭代方式和递归方式拥有相同的复杂度,但是迭代方式不需要附加的递归栈空间。
上述背包问题的递归写法:
#include <iostream>
#include <algorithm>
using namespace std;// 背包问题的递归解法
int f(int i, int capacity)
{// 参数 :// i:第i个物品// capacity: 背包剩余的容量int number_of_object = 3; // 物品的个数 int weight[] = {100, 14, 10}; // 每个物品的权重 int profit[] = {20, 18, 15};if(i == number_of_object){return (capacity < weight[number_of_object-1]?0:profit[number_of_object-1]); // 递归的终止条件 } if(capacity < weight[i-1]){return f(i+1, capacity);}return max(f(i+1, capacity), f(i+1, capacity-weight[i-1])+profit[i-1]);}int main(int argc, char *argv[])
{cout << f(1, 116) << endl;return 0;
}
上面的程序以递归的方式求解,递归需要额外的栈空间,下面对上述的递归方法做一下改进:
1. 在问题中,假设n=5, 物品的重量为weight = [2, 2, 6, 5, 4], 物品的权重profit = [6, 3, 5, 4, 6], 且c = 10;
求解 f(1, 10)的递归调用过程如下所示:
为了避免对 f(i,y) 的重复计算,可以建立一个列表对其值进行存储,在本题中,可以使用整形数组对数据进行存储,farray[i, y]
通过这种方法,可以将时间复杂度从原来的O(n^2)降到O(cn)
代码如下所示:
#include <iostream>
#include <algorithm>
using namespace std;// 背包问题的递归解法
int f(int i, int capacity)
{// 参数 :// i:第i个物品// capacity: 背包剩余的容量int number_of_object = 3; // 物品的个数 int weight[] = {100, 14, 10}; // 每个物品的权重 int profit[] = {20, 18, 15};if(i == number_of_object){return (capacity < weight[number_of_object-1]?0:profit[number_of_object-1]); // 递归的终止条件 } if(capacity < weight[i-1]){return f(i+1, capacity);}return max(f(i+1, capacity), f(i+1, capacity-weight[i-1])+profit[i-1]);} int f_1(int i, int capacity)
{// 参数// i: 剩余i, i+1,...n个物品// capacity: 剩余的背包容量// 利用矩阵farray[i][y]存储f(i,y)的值, 维度为(n+1)*(c+1) // n: 物品个数// c:背包的容量 int number_of_object = 5;int weight[] = {2, 2, 6, 5, 4};int profit[] = {6, 3, 5, 4, 6};int n = 5; int c = 10; int** farray = new int*[n+1];for(int k=0; k<=n; k++){farray[k] = new int[c+1];} for(int p=0; p<=n; p++){for(int q=0; q<=c; q++){farray[p][q] = -1;}}// 检查需要的值是否已经计算过了,如果已经计算过了,则直接返回 if(farray[i][capacity] > 0){return farray[i][capacity];}// 还没有计算过 if(i==number_of_object){farray[i][capacity] = (capacity < weight[i-1]?0:profit[i-1]);return farray[i][capacity];}if(capacity<weight[i]){farray[i][capacity] = f(i+1, capacity);} else{farray[i][capacity] = max(f(i+1, capacity), f(i+1, capacity-weight[i-1])+profit[i-1]);}int result = farray[i][capacity];// 释放内存for(int k=0; k<=n; k++){delete [] farray[k];} delete [] farray;return result; // 返回计算的值
}int main(int argc, char *argv[])
{cout << f_1(1, 10) << endl;return 0;
}
矩阵乘法链
问题描述:
矩阵A(mxn)与矩阵B(nxp)相乘需用时,将mnp作为两个矩阵相乘所需时间的测量值,假设要计算多个矩阵的乘积,一种是(AB)C,另一种是A(BC),虽然计算结果相同,但是时间性能会有很大的差异。
假设:
A(100x1), B(1x100), C(100x1)
1. (A*B)*C
所需时间 t=100*1*100 + 100*100*100 = 1010000, 此外,计算A*B需要100x100的矩阵来存储中间结果;
2.A*(B*C)
所需时间 t=1x100x1 + 100x1x1, 且只需要一个存储单元来存储中间结果:
将问题推广到一般的情况下,假设有q个矩阵相乘,M1xM2XM3....xMq, 则相乘的方式随着q的增加以指数及增长,因此,当q很大的时候,考虑每一种乘法顺序并选择最优者是不切实际的。
动态规划方法:
可以使用动态规划来求解一个矩阵相乘的乘法顺序,动态规划可以将算法的时间复杂度降为O(q^3)
C++数据结构与算法 动态规划相关推荐
- Python数据结构与算法-动态规划(钢条切割问题)
一.动态规划(DP)介绍 1.从斐波那契数列看动态规划 (1)问题 斐波那契数列递推式: 练习:使用递归和非递归的方法来求解斐波那契数列的第n项 (2)递归方法的代码实现 import time # ...
- 数据结构与算法-动态规划
题目描述: 1.一只青蛙一次可以跳上1级台阶,也可以跳上2级.求该青蛙跳上一个n级的台阶总共有多少种跳法. 2.一只青蛙一次可以跳上1级台阶,也可以跳上2级--它也可以跳上n级.求该青蛙跳上一个n级的 ...
- 数据结构与算法——动态规划思想解题三角形问题
动态规划的基本思想 动态规划解题的时候将问题分解为几个不同的阶段(把原始问题分解为不同的子问题),自底向上计算.每次决策都依赖于当前的状态.我们可以将不同阶段的不同状态存储在一个二维数组中. 从这个二 ...
- [数据结构与算法]动态规划:扔鸡蛋问题
参考链接:https://gitee.com/lambertcao/fucking-algorithm/blob/master/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92 ...
- 数据结构与算法--动态规划资源
爬楼梯:http://www.sohu.com/a/149075950_684445 01背包问题:https://juejin.im/post/5affed3951882567161ad511
- 数据结构与算法之暴力递归改动态规划
数据结构与算法之暴力递归改动态规划 目录 二维数组最小路径和 暴力递归改动态规划解析 任意选择数组arr中的数字,看能不能累加得到aim 1. 二维数组最小路径和 (一) 题目描述 (二) 思路 递归 ...
- 【数据结构与算法】【算法思想】动态规划
贪心算法 回溯算法 分治算法 动态规划 贪心:一条路走到黑,就一次机会,只能哪边看着顺眼走哪边 回溯:一条路走到黑,无数次重来的机会,还怕我走不出来 (Snapshot View) 动态规划:拥有上帝 ...
- python数据结构和算法 时间复杂度分析 乱序单词检测 线性数据结构 栈stack 字符匹配 表达式求值 queue队列 链表 递归 动态规划 排序和搜索 树 图
python数据结构和算法 参考 本文github 计算机科学是解决问题的研究.计算机科学使用抽象作为表示过程和数据的工具.抽象的数据类型允许程序员通过隐藏数据的细节来管理问题领域的复杂性.Pytho ...
- C++数据结构和算法2 栈 双端/队列 冒泡选择插入归并快排 二三分查找 二叉树 二叉搜索树 贪婪 分治 动态规划
C++数据结构和算法2 栈 双端/队列 冒泡选择插入归并快排 二三分查找 二叉树 二叉搜索树 贪婪 分治 动态规划 博文末尾支持二维码赞赏哦 _ github 章3 Stack栈 和 队列Queue= ...
最新文章
- 从源码分析DEARGUI之动态绘图的两种方法
- 内存管理中的几种地址类型
- JavaScript之事件的绑定与移除
- pycharm快捷使用
- linux自动化安装oracle,ftp的客户端软件 Linux环境一键自动化安装oracle软件的构想(附she...
- Lua中的metatable
- 深入到系统组件是否白盒测试_2019年系统集成项目管理工程师:第一章 信息化知识(三)要点?...
- 模型评价 - 机器学习与建模中怎么克服过拟合问题?
- python函数调用键盘热键_如何使用Python控制键盘和鼠标
- python-环境篇-Anaconda的安装
- openerp mac os 下 报表中文乱码问题。
- Windows内存管理(3)--检查内存可用性,结构化异常处理 和 ASSERT
- jmeter的安装和测试
- Win10系统下的MSCOMCT2.OCX, THREED32.OCX等控件安装方法
- phpcms 下载数统计
- 在面试中如何回答面试官的问题
- ffmpeg命令详细说明
- 详解ZStack高级功能--裸金属服务
- 如何高效的学习高等数学
- poj3518(Prime Grap 欧拉筛+二分)
热门文章
- SpringCloud工作笔记078---SpringBoot中使用sping事件驱动模型
- 经典线程同步 互斥量Mutex的使用分析
- PicPick手册:绿色小巧功能多的截屏软件
- Jakarta-ORO正则表达式的运用
- ftk学习记(list篇)
- 驻马店市机器人科技馆_科技创新体验馆,科技馆机器人
- awk处理带有空格的字符串_五分钟入门文本处理三剑客grep awk sed
- 计算机受限制用户,由于该计算机受到限制,本次操作已被取消的解决办法
- matlab硬币覆盖问题和八皇后,八皇后问题matlab程序
- .net单点登录demo_图文并茂,为你揭开“单点登录“的神秘面纱