递归、分治和动态规划的关系
2017-12-10 17:27
281 查看
内容会持续更新,有错误的地方欢迎指正,谢谢!
有大量的重复子问题
子问题之间有依赖(不独立)
与递归的关系:这些重复的子问题,DP算法将其结果用一维或二维数组(邻接矩阵)保存下来,等下一次又要计算该子问题时,直接用已计算好的;而递归却不是这样,它会一遍又一遍地计算这些重复的子问题,从而效率狂降。子问题重复率较高的递归算法可改写成动态规划,但不是所有递归算法都适合改成动态规划。
与分治的关系:在分治法中,有大量的重复子问题,且它们之间无依赖。
2.自底向上的动态规划实现:用的迭代。
2.自底向上的动态规划实现:会记录重复子问题结果的改进版迭代。只要有存储已经计算出的值的空间,就能把这项技术应用到任何递归计算中,就能把算法从指数级运行时间向线性时间改进。
3.自顶向下的动态规划实现:会记录重复子问题结果的改进版递归。存储它所计算的每一个值(正如下方代码最末的步骤),并通过检查所存储的值,来避免重新计算它们的任何项(正如最初的步骤)。
要先写出递推式,递推式:
n=0时,f(n)=0,即在f(n)函数中return 0
n=1时,f(n)=1,即在f(n)函数中return 1
n>1时,f(n)=f(n-1)+f(n-2),即在f(n)函数中return f(n-1)+f(n-2)
再用array数组来记录计算出的结果,避免重复计算一些值。
当然,用自顶向下的实现方式,并用数组记录计算过的值,一样可以避免重复的计算,也行~
动态规划
如果大问题分解为很多小问题后,小问题有互相重叠部分,则用递归的思路来分析问题,再用数组存储中间结果+循环的思路来写代码!动态规划的三个特征
适用于最优解问题有大量的重复子问题
子问题之间有依赖(不独立)
与递归的关系:这些重复的子问题,DP算法将其结果用一维或二维数组(邻接矩阵)保存下来,等下一次又要计算该子问题时,直接用已计算好的;而递归却不是这样,它会一遍又一遍地计算这些重复的子问题,从而效率狂降。子问题重复率较高的递归算法可改写成动态规划,但不是所有递归算法都适合改成动态规划。
与分治的关系:在分治法中,有大量的重复子问题,且它们之间无依赖。
如何写动态规划题目的代码
我觉得动态规划题目是很简单的,因为只要推出了递推式,什么0-1背包问题、作业排程问题、最长共同子序列LCS问题等等,代码根据递推式便可一气呵成。那又如何写分治题目的代码
举例:动态规划的两种类型
1.自顶向下的动态规划实现:用的递归。2.自底向上的动态规划实现:用的迭代。
斐波拉契数列
1.普通的递归实现的动态规划:效率特别低,有大量的重复计算,指数级的时间复杂度。int Fibo(int n) { if(n==0) return 0; if(n==1) return 1; return Fibo(n-1)+Fibo(n-2); }
2.自底向上的动态规划实现:会记录重复子问题结果的改进版迭代。只要有存储已经计算出的值的空间,就能把这项技术应用到任何递归计算中,就能把算法从指数级运行时间向线性时间改进。
int Fibo(int n) { int temp ; temp[0]=0; temp[1]=1; for(int i=2;i<n+1;++i) { temp[i]=temp[i-1]+temp[i-2]; } return temp ; }
3.自顶向下的动态规划实现:会记录重复子问题结果的改进版递归。存储它所计算的每一个值(正如下方代码最末的步骤),并通过检查所存储的值,来避免重新计算它们的任何项(正如最初的步骤)。
要先写出递推式,递推式:
n=0时,f(n)=0,即在f(n)函数中return 0
n=1时,f(n)=1,即在f(n)函数中return 1
n>1时,f(n)=f(n-1)+f(n-2),即在f(n)函数中return f(n-1)+f(n-2)
再用array数组来记录计算出的结果,避免重复计算一些值。
#include <iostream> #include <string.h> using namespace std; #define N 12 int array = {0}; int Fibo(int n) { //不等于初始值0,则表示该元素已经求解过了,直接用其值即可。 if(array !=0) return array ; //完成按着递推式来写逻辑,即可! if(n==0) return array = 0; if(n==1) return array = 1; if(n>1) return array = Fibo(n-1)+Fibo(n-2); } int main() { memset(array,0,sizeof(array)); cout << Fibo(N) << endl; return 0; }
总结:
为了避免递归产生的重复计算,多采用从下而上的迭代实现。所以,一般用自顶向下的递归思路来分析问题,并用自底向上的迭代思路来实现问题。自底向上也就是像求解斐波拉契数列那样,先给出f(0)和f(1)这些已知数值,再在循环里自底向上求解f(2)~f(n)的值,最后返回f(n)即可。当然,用自顶向下的实现方式,并用数组记录计算过的值,一样可以避免重复的计算,也行~
相关文章推荐
- 递归与动态规划关系
- 格子取数问题的动态规划和递归解法之比较
- 动态规划——递归转动态规划三部曲
- 动态规划 递归 例子 string 回文串-最长公共子串
- 动态规划+递归(斐波那契数)
- HDU 2041 超级楼梯 简单DP动态规划 递归
- 最长公共子序列求解:递归与动态规划方法
- 动态规划与递归非递归
- nyoj90 整数划分 动态规划与分治解法
- 分治策略、动态规划、贪婪选择以及递归之间的关系与区别(二)
- 背包问题---递归及动态规划
- Java企业面试算法新得体会之4递归和动态规划问题17问
- C#实现递归矩阵连乘(动态规划的递归自顶向下,非递归自地向上)
- 最长公共子序列求解:递归与动态规划方法
- 动态规划之最大连续子数组(递归备忘录写法)
- 我的第一个动态规划程序(试图用递归求斐波拉契数)
- 子集和是否存在问题的动态规划和递归解
- 算法原理与实践(动态规划与递归)
- 最大连续子序列和 分治思想和动态规划思想
- 动态规划:0-1背包问题(使用递归方法)