接上文:【README1】动态规划之解题思路

文章目录

  • 斐波那契数列讲解——解决重叠子问题
    • (1)暴力递归
    • (2)带有备忘录的递归解法
    • (3)自底向上——dp数组解法
    • (4)总结:状态转移方程
    • (5)状态压缩

斐波那契数列讲解——解决重叠子问题

所有的理论都需要实际的题目来验证,这里我们不选那些经典的动态规划的题目,因为我们只求抓住最本质,把这个思路讲解清楚,而斐波那契数列就是这样一个很好的例子,当然它并不算是一个非常典型的动态规划的题目。

LeetCode 509:斐波那契数列

接下来,我们会用各个方面来阐述,从时间复杂度最高的递归,再到备忘录和dp数组的解决,逐步讲解动态规划中我们应该怎么去想和怎么优化

(1)暴力递归

斐波那契数列是一个典型的递归题目,它的写法非常简单,这里我就不多强调了

class Solution {public:int fib(int n) {if (n==0) return 0;if (n==1 || n==2) return 1;return fib(n-1)+fib(n-2);}
};

题目可以通过,但是时间高的吓人

为什么这么慢呢?其实这就是动态规划的第一个大的问题——重叠子问题

我们知道递归问题本质就是二叉树的问题,所以在递归的过程中就是在遍历一颗二叉树,所以这种接法对应的递归树是这样的

很显然,想要计算fib(20),就先要计算fib(19)和fib(18),计算fib(19)又要计算fib(18)和fib(17)…可以看出图中的f(18),f(17)很显然是不需要计算的,虽然图中只画出了一点,但是你应该明白,这颗递归树要是完全展开,那是很恐怖的。所以这个算法极其低效

(2)带有备忘录的递归解法

既然耗时的原因是因为重叠子问题太多,那么不如这样:把fib(18)计算完之后,存储在一个备忘录中,下次只要需要求解fib(18),直接从备忘录里面拿多好
而实现备忘录,我们更多用数组,当然你也可以用哈希表,道理是一样的。

class Solution {public:int back(vector<int>& memory,int n){if(n==1 || n==2) return 1;if(memory[n]!=0) return memory[n];//一旦对应位置不等于0,表示已经计算过了,立马直接返回结果即可return back(memory,n-1)+back(memory,n-2);}int fib(int n) {if (n==0) return 0;vector<int> memory(n+1,0);//设立一个备忘录,初始状态设置为0,0表示该位置的元素没有被记录在备忘录上return back(memory,n);}
};

继续观察这种算法的递归树,如下,你可以很明显的发现,这种带备忘录的解法就是我们经常说的“剪枝”操作,以下把一颗非常冗余的树修剪的很干净,或者说就是减少了重叠子问题

如下,很明显它的时间复杂度要低

至此,这种算法的效率已经能符合动态规划相应的题目的效率要求了。但是我们现在的解决时自顶向下,而一般的动态规划的题目时自底向上的。

  • 自顶向下:从一个原始的问题,也就是规模较大的问题,比如说fib(20),逐步向下分解,分解到很小的规模,一直分解到再不能分解为止,比如fib(1)和fib(2)这两个base case
  • 自底向上:反过来,从最下面,最简单的情况如上,进行反推得到结果。我们动态规划就是按照这种思路来的

(3)自底向上——dp数组解法

我们把上面的备忘录变成一个dp数组,通过dp数组不断迭代向上求解。

class Solution {public:int fib(int n) {if(n==0) return 0;if(n==1 || n==2) return 1;vector<int> dp(n+1,0);//最简单的情况,base casedp[1]=1;dp[2]=1;for(int i=3;i<=n;i++){dp[i]=dp[i-1]+dp[i-2];}return dp[n];}
};

你会很明显发现,这样的结局效率非常的高

同时这种算法对应的图和带备忘录的递归算法的那张图结果相反

(4)总结:状态转移方程

其实在这个过程中,我们已经不知不觉的使用了状态转移方程。如下,

你可以把f(n)当作一个状态,而这个状态n是由状态n-1和状态n-2进行相加操作转移过来的,仅此而已。所以代码中的dp[i]=dp[i-1]+dp[i-2]就是这个意思

那么如何得出状态转移方程呢?其实完全依赖的就是咋们的暴力解法,暴力解法代表的就是状态转移方程,一旦动态规划的题目中你能写出暴力解法,那么剩余的操作无非就是备忘录或dp数组优化了。也就是先自顶向下,再自底向上

(5)状态压缩

至此我们已经解决了重叠子问题,关于如何解决最优子结构会在下篇文章中说明。
对于刚才的例子,还能继续进行优化,就是优化空间,因为在这个斐波那契数列中,当前状态仅仅和前两个状态有关,而前面无关,所以我们可以优化空间,不采用数组,直接两个变量,交替保存。

class Solution {public:int fib(int n) {if (n==0) return 0;if (n==1 || n==2) return 1;int prev=1;int curr=1;for(int i=3;i<=n;i++){int sum=prev+curr;prev=curr;curr=sum;}return curr;}
};

所以如果我们发现每次状态转移的时候只需要dp数组中的一部分,那么就可以尝试采用状态压缩来压缩dp table的大小,只记录必要的数据

【README2】动态规划之斐波那契数列说明重叠子问题如何解决相关推荐

  1. 动态规划实现斐波那契数列求解

    int fibonacciDP1(int n) { // 动态规划不使用数组int a = 0;int b = 1;int sum;int i;if (n == 0)return 0;if (n == ...

  2. 动态规划:斐波那契数列里面的东西?

    斐波那契数列 我想每个人都会写斐波那契数列吧!! 斐波那契数列的定义 f(0) = 1,f(1) = 1,f(n) = f(n-1) + f(n-2) 基于递归的方式实现,讲到递归都会用到: def ...

  3. 动态规划Ⅰ:斐波那契数列

    动态规划题目类型 & 做题思路总览:动态规划解题套路 & 题型总结 & 思路讲解 文章目录 一.斐波那契数列 1. 爬楼梯问题 2. 打家劫舍系列 3. 信件错排 4. 母牛生 ...

  4. LeetCode——动态规划:斐波那契数列

    斐波那契数列 目录 爬楼梯 强盗抢劫 强盗在唤环形街区抢劫 信件错排 注:具体解析请点击链接进入LeetCode题解区. 1. 爬楼梯 https://leetcode-cn.com/problems ...

  5. Python|动态规划问题--斐波那契数列

    欢迎点击「算法与编程之美」↑关注我们! 本文首发于微信公众号:"算法与编程之美",欢迎关注,及时了解更多此系列文章. 欢迎加入团队圈子!与作者面对面!直接点击! 斐波那契数列 斐波 ...

  6. C#递归、动态规划计算斐波那契数列

    //递归public static long recurFib(int num){if (num < 2){return num;}else{return recurFib(num - 1) + ...

  7. 剑指offer面试题10- II. 青蛙跳台阶问题(动态规划)(递归)(斐波那契数列)

    题目描述 一只青蛙一次可以跳上1级台阶,也可以跳上2级台阶.求该青蛙跳上一个 n 级的台阶总共有多少种跳法. 答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008, ...

  8. 斐波那契数列(剑指offer 10-I)

    一.题目描述 写一个函数,输入 n ,求斐波那契(Fibonacci)数列的第 n 项(即 F(N)).斐波那契数列的定义如下: F(0) = 0,   F(1) = 1 F(N) = F(N - 1 ...

  9. 深入剖析斐波拉契数列

    深入剖析斐波拉契数列 前言 动态规划作为一种非常经典的一类算法,不仅在解决实际问题当中有很多实际的应用,同时通常也是面试的一个重点.本篇文章一步步剖析动态规划的基本原理,通过斐波拉契数列问题(优化时间 ...

最新文章

  1. 机器学习PAL数据可视化
  2. 【NodeJS 学习笔记04】新闻发布系统
  3. asp正则替换链接实现伪静态效果
  4. 2019年1月已到,Java 8 要收费了吗?
  5. php暂停循环,在特定数量的递归循环后,PHP停止执行
  6. 文远知行2020年校招 | 领航者计划
  7. Java设计模式学习总结(15)——行为型模式之责任链模式
  8. 全国首家百度大脑创新体验中心启动,中关村创业大街AI产业创新生态再升级...
  9. 1225 八数码难题
  10. linux下往外ping不通-出现ping: unknown host www.baidu.com
  11. Netty5+Jboss(Marshalling)完成对象序列化传输
  12. 使用光学鼠标传感器实现旋转(或线性)测量(转)
  13. 2020年下半年软件设计师上午真题及答案解析(个人见解+网络解答+持续更新)
  14. 【图像处理】基于灰度矩的亚像素边缘检测方法理论及MATLAB实现
  15. Android蓝牙自动配对和Pin码设置
  16. Binder机制之Service Manager(大内总管)
  17. 2021年安全员-A证报名考试及安全员-A证考试资料
  18. 人在旅途——》张家界之旅:20180419
  19. 一张图数字孪生北京大兴机场
  20. 查看实时网速的小方法

热门文章

  1. android 初始化语言,25.Android init language (安卓初始化语言)
  2. ROS服务中自定义数据类型
  3. 写作有困扰?不知道用什么词?不知道怎么解释不一致的结果?这个网站来帮你。
  4. Python学习之路和隐藏特征
  5. java 32位_Java知识点:创建节约内存的JavaBean
  6. 华为交换机eth口作用_华为S5700交换机如何配置ETH-交换机怎么设置
  7. 基于java的订餐系统设计(含源文件)
  8. python3 集合运算_Python 集合与集合运算
  9. esp8266原理图_ESP8266_22基于自身ADC的电压采样
  10. MySQL笔记-group by和聚合函数的使用