一、动态规划介绍

动态规划遵循一套固定的流程:递归的暴力解法→带备忘录的递归解法→非递归的动态规划解法递归的暴力解法\to带备忘录的递归解法\to非递归的动态规划解法递归的暴力解法→带备忘录的递归解法→非递归的动态规划解法,这是一个层层递进的解决问题的过程。我们见的多了写的多了当然可以一步写出非递归的动态规划解法,但是在最开始练习时最好遵循这个过程。

以两个问题为例来说明递归问题:

1.1 斐波那契问题

leetcode-509-斐波那契数

1.1.1 暴力的递归算法

class Solution {public int fib(int n) {if(n<=1){return n;}else{return fib(n-1)+fib(n-2);}}
}

这种解法十分低效,通过画出递归树可以更直观的观察这种解法(PS:但凡遇到需要递归的问题,最好都画出递归树,这对我们分析算法的复杂度,寻找算法低效的原因都有巨大帮助):

递归算法的时间复杂度就是子问题的个数乘以解决一个子问题需要的时间。在这个问题中,二叉树节点总数为指数级别,所以子问题个数为O(2n)O(2^n)O(2n)。

解决一个子问题的时间为O(1),因为没有任何循环操作,所以这个算法的时间复杂度为O(2n)O(2^n)O(2n),为指数级别。

通过观察递归树,可以很明显地发现算法低效的原因:存在大量重复计算,比如f(18)被计算了两次,而且我们可以看到,以f(18)为根的这个递归树体量巨大,多算一遍,会耗费巨量的时间。更何况不止f(18)这一个节点被重复计算,所以这个算法及其低效。

这就是动态规划问题的第一个性质:重叠子问题,下面解决这个问题。

1.1.2 带备忘录的递归解法

明确了耗时的原因是重复计算后,我们可以造一个”备忘录“,每次算出某个子问题的答案后不着急返回,而是先记录到”备忘录“中;同理,每次遇到一个子问题先去”备忘录“里面查一下,如果发现之前已经解决过这个问题了,直接把答案拿出来用,不要再耗时去进行计算。

一般使用一个数组充当这个”备忘录“,当然我们也可以使用哈希表(字典),思想都是一样的。

class Solution {int[] mem;public int fib(int n) {if(n<=1){return n;}mem=new int[n+1];mem[0]=0;mem[1]=1;for(int i=2;i<=n;i++){mem[i]=-1;}helper(mem,n);return mem[n];}void helper(int[] mem,int n){if(mem[n]==-1){if(mem[n-2]==-1){helper(mem,n-2);}if(mem[n-1]==-1){helper(mem,n-1);}mem[n]=mem[n-2]+mem[n-1];}}}

现在,画出递归树,我们就可以知道备忘录做了什么:

实际上,带”备忘录”的递归算法,把一颗存在巨量冗余的递归树通过“剪枝”,改造成了一个不存在冗余的递归图,极大减少了子问题(即递归图中节点)的个数。

这时子问题个数的计算相对简单,因为不存在冗余计算,子问题就是数量就是O(n)O(n)O(n),而解决一个子问题的时间就是O(1)O(1)O(1),所以本算法的时间复杂度为O(n)O(n)O(n)。因此,这个算法的时间复杂度就为O(n)O(n)O(n)。

制词,这种带备忘录的递归解法的效率已经和动态规划一样了。实际上,这种解法和动态规划的思想已经差不多了,只不过这种方法叫做“自顶向下”,动态规划叫做“自底向上”。

自顶向下:刚才画的递归树是从上向下延伸,是从一个规模较大的原问题(f(20),向下逐渐分解规模,直到f(1)和f(2)触底,然后逐层返回答案,这就叫做“自顶向下”

自底向上:反过来,我们直接从最底下,最简单,问题规模最小的f(1)和f(2)开始往上推,直到推到我们想要的答案f(20),这就是动态规划的思路,这也是为什么动态规划一般都脱离了递归,而是由循环迭代完成计算。

1.1.3 动态规划

有了上一步“备忘录”的启发,我们可以把这个“备忘录”独立出来成为一张表,就叫做DP table,在这张表上可以进行自底向上的推算:

class Solution {public int fib(int n) {if(n<=1){return n;}int[] dp=new int[n+1];dp[0]=0;dp[1]=1;for(int i=2;i<=n;i++){dp[i]=dp[i-1]+dp[i-2];}return dp[n];}
}

用一张图来理解自底向上的过程:

可以发现,这个DP table特别像之前那个剪枝后的结果,只是反过来了。实际上,带备忘录的递归解法中的“备忘录”,最终完成后也就是这个DP table,所以说这两种解法其实是差不多的,大部分情况下,效率也基本相同。

在这里引出状态转移方程,这实际上就是描述问题结构的数学形式:

可以看出,状态转移方程直接代表着暴力解法。但是不要看不起暴力解,动态规划问题最困难的就是写出状态转移方程,即这个暴力解。优化方法无非是用备忘录或者DP table。

这个例子可以通过一个细节进行优化。根据状态转移方程,当前状态只和之前两个状态有关,其实并不需要一个数组来存储所有的状态,只要存储之前的两个状态就可以了。所以,可以进一步进行优化,将空间复杂度降为O(1):

class Solution {public int fib(int n) {int x1=0;int x2=1;if(n<=1){return n;}int ret=1;for(int i=2;i<=n;i++){ret=x1+x2;x1=x2;x2=ret;}return ret;}
}

严格来说,斐波那契数列的例子并不算动态规划,因此没有体现出动态规划的另一个重要特性即“最优子结构”。当问题中要求求出一个最优解或者在代码中看到循环和max、min等函数时,十有八九需要动态规划。

1.2 找零钱问题

leetcode动态规划相关推荐

  1. LeetCode 动态规划《简单》部分 Python实现

    #2018-06-07 June Thursday the 23 week, the 158 day SZ #LeetCode 动态规划简单部分 Python实现 '''爬楼梯 假设你正在爬楼梯.需要 ...

  2. Leetcode动态规划部分典型题目分类及总结

    参考内容 https://leetcode-cn.com/problems/longest-palindromic-substring/solution/zhong-xin-kuo-san-dong- ...

  3. [LeetCode]动态规划求解博弈问题

    博弈论是有趣又有用的知识,可以用来预测在特定的规则下,人们会做出怎样的行为,又会导致怎样的结果.利用博弈论来指导人们的行事法则甚至商业操作,比如著名的囚徒困境就被很好的利用在了商业竞争上.同样,Lee ...

  4. LeetCode动态规划股票系列整理

    写在前面 股票感觉是LeetCode动态规划中系列最多的一类,交易次数不同,有冷冻期,含手续费,让买卖的最佳时机千奇百怪,但是只要掌握dp的方法,解决起来还是有套路可循的.依据dp的常规思想,股票问题 ...

  5. LeetCode 动态规划(Dynamic programming)系列题目--C++,Python解法

    LeetCode上有许多态规划(Dynamic programming)的题目,我在这里整合一下 本文章不再更新,请看LeetCode 所有题目总结 LeetCode 所有题目总结:LeetCode ...

  6. 化工热力学补考成功,几天没有头脑了,赶紧赏自己几题Leetcode动态规划算法最长系列

    @Author:Runsen @Date:2020/10/9 "恭喜你昨天,化工热力学补考成功!" "区区化工热力学还想让我重修,只不过浪费了我九月一半的精力和十月的九成 ...

  7. leetcode动态规划(python与c++)

    1 . 斐波那契数 class Solution:def fib(self, n: int) -> int:# if n==0:# return 0# elif n==1:# return 1# ...

  8. Leetcode动态规划:300.longest-increasing-subsequence(最长递增子序列)

    300. 最长递增子序列 最近一直在攻克动态规划的题,Leetcode的简单题已经刷完,现在冲中等题,这道题算是一个比较经典的题吧,独立完成,虽然花了两个多小时,但收获很多: 思路:动态规划首先要找到 ...

  9. 从零开始刷Leetcode——动态规划(70.198.303)

    文章目录 70. 爬楼梯 198. 打家劫舍 303. 区域和检索 - 数组不可变 动态规划属于热门问题,在leetcode中主要以medium和hard为主. 70. 爬楼梯 假设你正在爬楼梯.需要 ...

  10. Leetcode动态规划题解1——两要素和解题步骤

    动态规划概述 动态规划,是一种解决最优化问题的方法,在我看来就是一种穷举算法.只不过,这种穷举算法具有"特殊性质".一般的穷举法,就是列出问题所有的可行解,然后通过比较找到最优解, ...

最新文章

  1. Kafka 顺序消费方案
  2. GitHub 热榜:中国博士开发可交互全球疫情地图,登上柳叶刀!
  3. 用 Python 快速实现 HTTP 和 FTP 服务器
  4. Jenkins的卸载
  5. linux系统下开机启动流程
  6. 安装了但是报错找不到_安装MySQL时由于找不到vcruntime140_1.dll,无法继续安装
  7. 前端学习(1951)vue之电商管理系统电商系统之获取父级数据列表
  8. 忽然感觉公司的工作环境有污染,墙壁和地面会散发异味,时间长了会头疼。...
  9. 2019.03.25 bzoj4539: [Hnoi2016]树(主席树+倍增)
  10. python应用系列教程——python使用smtp协议发送邮件:html文本邮件、图片邮件、文件附件邮件
  11. [osx] android studio下修改avd的hosts文件
  12. axios 文档中文翻译
  13. Linux常用命令——rsync
  14. c++ 编程规范技巧
  15. 暮光之城4下高清下载地址|暮光之城4第二部高清下载地址
  16. 远程teamviewer|远程工具teamviewer|远程控制软件teamviewer
  17. 如何用CSS把正方形变成圆形
  18. 垃圾场恶臭环境监测系统方案
  19. “辣条一哥”冲刺IPO,卫龙三年净赚近20亿,小辣条赚大钱
  20. Allegro自动对齐工具

热门文章

  1. php做页面编辑器,最牛在线编辑器ueditor在thinkphp框架中的使用方法
  2. java继承的性质,浅谈Java三大特性之继承
  3. Android ViewFilpper实现分页效果
  4. vlc_for_android(基于git-3.0.0)快速集成并播放电视节目直播
  5. Android View框架总结(五)View布局流程之Layout
  6. java数组数据结构_Java数据结构一维数组的应用
  7. tomcat与mysql分离部署_apache+tomcat+mysql 实现动静分离
  8. w10恢复出厂设置_Win10系统恢复出厂设置和重装系统有什么区别?
  9. pci系列微型计算机,PCI系列586/60微型计算机,其中PCI是()。
  10. python 控制流