看了本文还入不了门,那就基本告别动态规划了 : ) ~

一、简介

二、【能用】动态规划解决的问题

三、【适合用】动态规划解决的问题

四、动态规划求解的3个关键步骤

1. 建立状态转移方程

2. 缓存小规模答案以复用,避免重复计算

3. 按规模顺序从小到大求解,且最小的几个答案必须手动给出

五、实例

实例1 —— 斐波那契数列

实例2 - 不同路径(leetcode no.62中等难度)

实例3 - 爬楼梯(leetcode no.70 简单)

实例4 - 三角形最小路径和(leetcode no.120 中等)

一、简介

  • 动态规划 其实是 【高中数列问题】,目的是通过f(1)~f(n-1)求解f(n);
  • 区别是高中的数列问题往往通过f(n)的显式通项公式求解;而许多数列问题无法直接求解出f(n)的显式通项公式,但易知【状态转移方程】(即通过f(1)~f(n-1)求解f(n)),动态规划基于转移方程使用计算机来递推出f(n)。
  • 改为【递推存储法】可能更符合算法的本意。

二、【能用】动态规划解决的问题

满足以下两点的问题,即可用动态规划求解

1. 问题的答案,依赖于问题的规模;所有问题对应的答案构成了一个【数列】。

例如,n个人有几条腿?所有问题的答案构成数列:f(1)=2, f(2)=4, ... , f(n)=2n。其中n就是问题的规模,问题的规模确定了(n指定了),答案也就确定了。

2. 大规模的答案,可由小规模的答案递推得到。

例如,n个人有几条腿?可由 f(n) = f(n-1) + 2 得到

三、【适合用】动态规划解决的问题

能用动态规划解决,不代表适合用动态规划解决。当问题的答案无法直接通过求解【显示式子】(又称通项公式)得到时,适合用动态规划求解。

例如,n个人有几条腿?存在显示公式f(n)=2n,则无需使用动态规划

四、动态规划求解的3个关键步骤

1. 建立状态转移方程

  • 核心思想是利用 f(1)~f(n-1) 推导出f(n)
  • 例如,n个人有几条腿?的状态转移方程为 f(n) = f(n-1)+2。

2. 缓存小规模答案以复用,避免重复计算

必须将 f(1)~f(n-1)的答案缓存,这是减少时间复杂度的本质,避免重复计算

3. 按规模顺序从小到大求解,且最小的几个答案必须手动给出

  • 套娃必须从小套到大;
  • 但得有几个已知的起始点;已知起始点也可用于check状态转移方程是否正确

五、实例

实例1 —— 斐波那契数列

菲波那切数列:0,1,2,3,5,....

状态转移方程:f(n) = f(n-1) + f (n-2)

求任意f(n)

# -*- coding: utf-8 -*-# 递归 O(n^2)
def fib(n):if n <= 1:return 0if n == 2:return 1 # f(n=1)=1else:return fib(n-1) + fib(n-2)# for循坏 —— 优化空间使用的【动态规划】 O(n)
def fib2(n):ans_minus_2 = 0ans_minus_1 = 1if n <= 1:return ans_minus_2if n == 2:return ans_minus_1  # f(n=1)=1else:for i in range(4, n+1):tmp = ans_minus_1ans_minus_1 = ans_minus_2 + ans_minus_1  # if n=i, f(n-1) update 【此处使用了状态转移方程】ans_minus_2 = tmp  # if n=i, f(n-2) updatereturn ans_minus_1 + ans_minus_2# 知乎博客版,完全对应动态规划的 各个步骤
def fib3(n):res = list(range(n))   # 用于缓存各个步骤结果for i in range(1, n):  # 从小到大计算if i <= 2:res[i] = i-1else:res[i] = res[i-1] + res[i-2]  # 状态转移方程return res[n-1] + res[n-2]# ----------------------------------------------------------------------
# 打印各算法结果是否一致
for i in range(1, 10):print(str(i)+": "+ str(fib(i))+", "+str(fib2(i))+", "+str(fib3(i)))
print("\n---------\n")# 时间效率比较
from timeit import timeit
# 1. 递归,低效的原因是 递归把数列中的所有数字都拆解成f(0)和f(1)了,且拆解方法极其冗余
print("time cost [recursive algo]: %f" % timeit(stmt="fib(30)", setup="from __main__ import fib", number=3))
# 2. 原始动态规划
print("time cost [dp algo]: %f" % timeit(stmt="fib3(30)", setup="from __main__ import fib3", number=3))
# 3. 优化了空间效率的动态规划,只存储f(n-1)和f(n-2)
print("time cost [优化空间效率的 dp algo]: %f" % timeit(stmt="fib2(30)", setup="from __main__ import fib2", number=3))
# time cost [recursive algo]: 0.897780
# time cost [dp algo]: 0.000026
# time cost [优化空间效率的 dp algo]: 0.000009print(fib2(100))  # 218922995834555169026

【递归】为什么低效,时间复杂度为O(n^2), 以fib(6)的计算为例,递归是这么算的,存储了每个叶子节点:

实例2 - 不同路径(leetcode no.62中等难度)

一个矩形表格,m*n个格子(下图是7*3),每次只能【向下或者向右】走一格,问从左上角 走到 右下角,共有多少种不同的路径?

1. 推导状态转移方程:f(m,n) = f(m-1, n) + f(m, n-1)

2. 缓存并复用以往结果:此处是个二维数列,需用二维列表

3. 从小到大遍历:两个for,遍历长和宽;

f(1, n) = 1

f(m, 1) = 1

'''
leetcode no.62 不同路径
'''
def unique_path_num(m, n):# 1. 初始化二维列表以存储历史数据res = [[1] * n] * m # 快速创建定长list, 优于[[0]*n for i in range(m)]# 2. 从小到大两层遍历,注意要手动指定初始答案值for i in range(m):if i == 0:res[i] = [1] * n  # 手动指定初始答案值continuefor j in range(n):if j == 0:res[i][j] = 1  # 手动指定初始答案值else:res[i][j] = res[i-1][j] + res[i][j-1]  # 3. 状态转移方程return res[-1][-1]print(unique_path_num(7,3))

实例3 - 爬楼梯(leetcode no.70 简单)

爬楼梯,n阶楼梯,每步可爬1阶或2阶,问有多少种不同的方法可以爬到楼顶?

1. 转移方程:

错误的方程是 f(n) = 2 * f(n-2) 从n-2到n有2种方法,但到n可以不经过n-2 !!!!

正确的方程是 f(n) = f(n-1)+f(n-2) 要么从n-1跨一步,要么从n-2跨2步,这有这两种可能!!

2. 1维list即可

3. 从小到大遍历,且有f(1)=1; f(2)=2

class Solution:def climbStairs(self, n: int) -> int:dp_m2 = 1dp_m1 = 2if n<1:return 0if n==1:return dp_m2if n==2:return dp_m1for i in range(3, n):tmp = dp_m1dp_m1 = dp_m1+dp_m2dp_m2 = tmpreturn dp_m1+dp_m2

实例4 - 三角形最小路径和(leetcode no.120 中等)

首先,从第顶层到最底层的最小路径 和 第底层到最顶层的最小路径 等价

求到任意一层的最小都必须考虑所有点,因此逐层贪心是不行的。

自底向上,则第i层第j个节点的最小路径 转移方程:

dp[i][j] = min(dp[i+1][j] +dp[i+1][j+1]) +triangle[i][j]

初始条件:

dp[-1] = triangle[-1]

class Solution:def minimumTotal(self, triangle: List[List[int]]) -> int:dp = [[0]*len(i) for i in triangle]for k in range(len(triangle)):i = len(triangle) -1 -kif k == 0:dp[i] = triangle[-1] # 底层的最小路径就是自己continuefor j,v in enumerate(triangle[i]):dp[i][j] = min(dp[i+1][j], dp[i+1][j+1]) + v  # 转移方程return dp[0][0]   # 顶点就是自底层到顶层的最小路径

# 注:可直接利用triangle数组来存dp的结果,可以省去开辟内存空间

Refer

https://www.zhihu.com/question/39948290/answer/883302989(思路,原理分析)

https://www.cnblogs.com/chaojunwang-ml/p/11365562.html(动态规划leetcode题目汇总分析)

【动态规划】 最简入门教程相关推荐

  1. Nginx 极简入门教程

    Nginx 极简入门教程 基本介绍 Nginx 是一个高性能的 HTTP 和反向代理 web 服务器,同时也提供了 IMAP/POP3/SMTP服务. Nginx 是由伊戈尔·赛索耶夫为俄罗斯访问量第 ...

  2. Python极简入门教程

    前言 为了方便各位小白能轻松入门Python,同时加深自己对Python的理解,所以创造了"Python极简入门教程",希望能帮到大家,若有错误请多指正,谢谢.极简入门教程代表着不 ...

  3. tensorflow平台极简方式_TensorFlow极简入门教程

    原标题:TensorFlow极简入门教程 随着 TensorFlow 在研究及产品中的应用日益广泛,很多开发者及研究者都希望能深入学习这一深度学习框架.本文介绍了TensorFlow 基础,包括静态计 ...

  4. pyecharts极简入门教程

    作者:luanhz 来源:小数志 导读 数据可视化是整个数据分析流程中的关键环节,甚至有着一图定成败的关键性地位.前期,陆续推出了matplotlib和seaborn详细入门教程,对于常规的数据探索和 ...

  5. pyecharts x轴字体大小调整_pyecharts极简入门教程

    导读 数据可视化是整个数据分析流程中的关键环节,甚至有着一图定成败的关键性地位.前期,陆续推出了matplotlib和seaborn详细入门教程,对于常规的数据探索和基本图表制作是足够的,但二者的一个 ...

  6. Docker 极简入门教程,傻瓜都能看懂!

    富 Web 时代,应用变得越来越强大,与此同时也越来越复杂.集群部署.隔离环境.灰度发布以及动态扩容缺一不可,而容器化则成为中间的必要桥梁. 本文我们就来探索一下 Docker 的神秘世界,从零到一掌 ...

  7. 机器学习极简入门教程(一)

    阅读此文,需要有python基础,有英文阅读能力的人点这里. part1 这里不回答机器学习是什么,能做什么,只告诉你如何快速上手. 熟悉你手中的数据. 1.常用模块导入命令 import panda ...

  8. [MCU][测试工具]MCU性能测试,CoreMark极简入门教程

    提起MCU性能测试,最著名的就是CoreMark和Dhrystone. CoreMark以每秒迭代次数作为性能评价,而Dhrystone的DMIPS与Dhrystone标准相关. 本文讨论CoreMa ...

  9. Powerlink协议使用极简入门教程

    网上关于powerlink协议的知识太少,而且都太分散太零碎,容易让人一头扎进去摸不着头绪.新手看到demo源码可能都不知道有什么用途,怎么能联动的跑起来看到效果,有种无从下手的感觉.其实Powerl ...

最新文章

  1. 在Linux上编译LLVM/Clang 8.0.0等全部源代码
  2. MASM6.1使用方法(适合初学者)
  3. 逆向工程核心原理读书笔记-API钩取之IE浏览器连接控制
  4. java获取页面标签_java获取网页源代码后,提取标签内容……
  5. 用python做频数分析_使用Python进行描述性统计
  6. opencv-api getPerspectiveTransform
  7. stl 优先队列(堆)
  8. 全数字伺服系统中位置环和电子齿轮的设计
  9. CCF NOI1032 菱形
  10. Atitit.常用的gc算法
  11. vfp mysql教程_VFP基础教程 5.5 编辑框控件(editbox)
  12. ubuntu安装WPS字体缺失的解决办法
  13. POJ 2240 Arbitrage——spfa判正环||flody
  14. MATLAB linspace函数
  15. 神州数码c语言笔试题,神州数码笔试题,神州数码笔试题.doc
  16. 妙算2的串口用自己的接线(杜邦线)连接无人机210或者stm32
  17. 腾讯加入“三月宕机全家桶”:系上海网络运营商光纤故障
  18. appleid注册服务器错误,连接apple id 服务器时出错(Apple ID 验证连接失败,试试这招)...
  19. Win10极简fliqlo时钟屏保(附下载链和安装教程)
  20. 什么是SaaS系统,SaaS系统有哪些优势

热门文章

  1. 华为mate20是android几,华为mate20x与mate20区别 mate20x好不好
  2. 达芬奇工具链的建立(工程编译步骤DM6446)
  3. 编写计算机软件,编写软件是一种艺术
  4. Virus驱逐舰杀毒无限升级版
  5. Qt编写自定义控件12-进度仪表盘
  6. Linux上使用at单一执行例行性工作,使用crontab循环执行例行性任务以及系统例行任务
  7. ZStack 云环境模拟器
  8. WARNING: WinPcap is now deprecated (not maintained). Please use Npcap instead
  9. 儿童语言发育迟缓分类中C群d是指,(S—S法)语言发育迟缓检查(CRRC版)
  10. linux 怎样清理内存,linux清理内存命令(示例代码)