【动态规划】 最简入门教程
看了本文还入不了门,那就基本告别动态规划了 : ) ~
一、简介
二、【能用】动态规划解决的问题
三、【适合用】动态规划解决的问题
四、动态规划求解的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题目汇总分析)
【动态规划】 最简入门教程相关推荐
- Nginx 极简入门教程
Nginx 极简入门教程 基本介绍 Nginx 是一个高性能的 HTTP 和反向代理 web 服务器,同时也提供了 IMAP/POP3/SMTP服务. Nginx 是由伊戈尔·赛索耶夫为俄罗斯访问量第 ...
- Python极简入门教程
前言 为了方便各位小白能轻松入门Python,同时加深自己对Python的理解,所以创造了"Python极简入门教程",希望能帮到大家,若有错误请多指正,谢谢.极简入门教程代表着不 ...
- tensorflow平台极简方式_TensorFlow极简入门教程
原标题:TensorFlow极简入门教程 随着 TensorFlow 在研究及产品中的应用日益广泛,很多开发者及研究者都希望能深入学习这一深度学习框架.本文介绍了TensorFlow 基础,包括静态计 ...
- pyecharts极简入门教程
作者:luanhz 来源:小数志 导读 数据可视化是整个数据分析流程中的关键环节,甚至有着一图定成败的关键性地位.前期,陆续推出了matplotlib和seaborn详细入门教程,对于常规的数据探索和 ...
- pyecharts x轴字体大小调整_pyecharts极简入门教程
导读 数据可视化是整个数据分析流程中的关键环节,甚至有着一图定成败的关键性地位.前期,陆续推出了matplotlib和seaborn详细入门教程,对于常规的数据探索和基本图表制作是足够的,但二者的一个 ...
- Docker 极简入门教程,傻瓜都能看懂!
富 Web 时代,应用变得越来越强大,与此同时也越来越复杂.集群部署.隔离环境.灰度发布以及动态扩容缺一不可,而容器化则成为中间的必要桥梁. 本文我们就来探索一下 Docker 的神秘世界,从零到一掌 ...
- 机器学习极简入门教程(一)
阅读此文,需要有python基础,有英文阅读能力的人点这里. part1 这里不回答机器学习是什么,能做什么,只告诉你如何快速上手. 熟悉你手中的数据. 1.常用模块导入命令 import panda ...
- [MCU][测试工具]MCU性能测试,CoreMark极简入门教程
提起MCU性能测试,最著名的就是CoreMark和Dhrystone. CoreMark以每秒迭代次数作为性能评价,而Dhrystone的DMIPS与Dhrystone标准相关. 本文讨论CoreMa ...
- Powerlink协议使用极简入门教程
网上关于powerlink协议的知识太少,而且都太分散太零碎,容易让人一头扎进去摸不着头绪.新手看到demo源码可能都不知道有什么用途,怎么能联动的跑起来看到效果,有种无从下手的感觉.其实Powerl ...
最新文章
- 在Linux上编译LLVM/Clang 8.0.0等全部源代码
- MASM6.1使用方法(适合初学者)
- 逆向工程核心原理读书笔记-API钩取之IE浏览器连接控制
- java获取页面标签_java获取网页源代码后,提取标签内容……
- 用python做频数分析_使用Python进行描述性统计
- opencv-api getPerspectiveTransform
- stl 优先队列(堆)
- 全数字伺服系统中位置环和电子齿轮的设计
- CCF NOI1032 菱形
- Atitit.常用的gc算法
- vfp mysql教程_VFP基础教程 5.5 编辑框控件(editbox)
- ubuntu安装WPS字体缺失的解决办法
- POJ 2240 Arbitrage——spfa判正环||flody
- MATLAB linspace函数
- 神州数码c语言笔试题,神州数码笔试题,神州数码笔试题.doc
- 妙算2的串口用自己的接线(杜邦线)连接无人机210或者stm32
- 腾讯加入“三月宕机全家桶”:系上海网络运营商光纤故障
- appleid注册服务器错误,连接apple id 服务器时出错(Apple ID 验证连接失败,试试这招)...
- Win10极简fliqlo时钟屏保(附下载链和安装教程)
- 什么是SaaS系统,SaaS系统有哪些优势
热门文章
- 华为mate20是android几,华为mate20x与mate20区别 mate20x好不好
- 达芬奇工具链的建立(工程编译步骤DM6446)
- 编写计算机软件,编写软件是一种艺术
- Virus驱逐舰杀毒无限升级版
- Qt编写自定义控件12-进度仪表盘
- Linux上使用at单一执行例行性工作,使用crontab循环执行例行性任务以及系统例行任务
- ZStack 云环境模拟器
- WARNING: WinPcap is now deprecated (not maintained). Please use Npcap instead
- 儿童语言发育迟缓分类中C群d是指,(S—S法)语言发育迟缓检查(CRRC版)
- linux 怎样清理内存,linux清理内存命令(示例代码)