1.动态规划常见题型:
①斐波那契
②路径问题
③背包问题
④打家劫舍
⑤股票问题
⑥子序列问题
2.动态规划步骤:
1.明确dp[i]的含义及下标含义
2.求递推公式
3.根据递推公式和题意确定如何初始化数组
4.确定遍历顺序
5.如果有bug,将dp数组打印出来debug
3.题目链接:509. 斐波那契数
题目描述:
斐波那契数 (通常用 F(n) 表示)形成的序列称为 斐波那契数列 。该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和。也就是:
F(0) = 0,F(1) = 1
F(n) = F(n - 1) + F(n - 2),其中 n > 1
给定 n ,请计算 F(n) 。
解法1维护数组:
1.思路五部曲:
①首先明确dp[i] --- 表示的是第i个位置的斐波那契数
②递推公式 --- dp[i] = dp[i-2] + dp[i-1]
③dp初始化 --- 根据题意dp[0] = 0,dp[1] = 1
④遍历顺序 --- 因为是根据前两个的值得到当前值,所以按顺序遍历
2.步骤:
①故先定义一个dp数组,长度为n+1,因为有0有n所以长度为n+1
②初始化dp[0] = 0, dp[1] = 1
③顺序遍历for(i=2~n){递推公式}
④最后返回数组中最后一个值。
⑤要注意的是要在最前面写if(n<2){return n;} --- 可能会想,下面不是都初始化了0,和1对应的值吗?为什么还要特殊写,因为如果不写的话在定义dp数组的时候,如果n = 0,那么数组的长度就是1,无法定义dp[1],就会出现索引越界的问题,所以一定要写。
解法2维护三个数:
可以将上面维护数组的动作压缩成只维护三个变量,即dp[i-2],dp[i-1],dp[i].用a,b,c分别对应,即a = 0,b = 1,c = 0。然后在for循环中{c = a+b, a = b, b = c},最后返回c。
下面为代码(java):
4.题目链接:70. 爬楼梯
题目描述:
假设你正在爬楼梯。需要 n
阶你才能到达楼顶。
每次你可以爬 1
或 2
个台阶。你有多少种不同的方法可以爬到楼顶呢?
解法:
1.思路5步曲:
①首先明确dp[i]表示的是,共有i个阶,有 dp[i]种方法爬到楼顶。
②递推公式:当i =1,有1种。当i=2,有2种,当i=3,有3种(怎么求的呢?当有3个台阶,上一个必须在其上一个或者上两个,因为每次只能爬1个或者2个台阶)故3 =1+2。所以递推公式就是,dp[i] = dp[i-1] + dp[i-2].
③初始化:根据递推公式可以发现,整个数组的基础就是前两个值,所以初始化前两个即可。即dp[1] = 1,dp[2] = 2.此处的dp[0]没有意义,即没有台阶那么要么理解成直接站到最终位置了需要0种方法,要么理解成1种方法---但题目中说了n>0所以这个赋值是没有意义的,但是这样初始化代码好写,我们不这样写,采取不赋值的写法。
④遍历顺序:根据递推公式,需要前面的值来求当前值,所以顺序遍历。
2.步骤:
①即初始化dp,长度为n+1,因为规定问题,虽然我们只需要n个元素,但是数组下标从0开始,所以长度为n+1
②初始化---dp[1] = 1,dp[2] =2
③顺序遍历---for(i=2开始~n){dp[i] = dp[i-2] + dp[i-1];}
④最后返回dp[n].
⑤要注意同样的越界问题,在最上面要写if(n<=2)|| if(n==1),{return n}这两种都不会越界。
⑥当然也可以进行压缩,只维护三个变量。
下面为代码(java):
4.题目链接:746. 使用最小花费爬楼梯
题目描述:
给你一个整数数组 cost ,其中 cost[i] 是从楼梯第 i 个台阶向上爬需要支付的费用。一旦你支付此费用,即可选择向上爬一个或者两个台阶。
你可以选择从下标为 0 或下标为 1 的台阶开始爬楼梯。
请你计算并返回达到楼梯顶部的最低花费
解法:
1.思路五步曲:
①明确dp[i]表示的是到第i个台阶需要的最低花费,因为cost[i]表示的是从楼梯第 i 个台阶向上爬需要支付的费用,所以当走到cost数组的最后一个位置之后再向上走一步才是楼顶,所以dp数组的长度应该是cost长度+1.
②递推公式:dp[i]只能由dp[i-1]或者dp[i-2]向上跳。因为每次只能跳一步或者跳两步,而又要求最小花费,故dp[i] = min(dp[i-1]+cost[i-1],dp[i-2]+cost[i-2])
③初始化:根据递推公式,整个数组的基础就是前两个值,所以初始化dp[0],dp[1]。而题目中说可以选择从0位置或者1位置开始爬,故dp[0] =0 ,dp[1] = 1
④遍历顺序:根据递归公式,要通过前面的值获取当前值,所以呢要顺序遍历。
2.步骤:
①构建dp数组,长度为cost长度+1
②初始化dp[0] = 0,dp[1] =0
③for(i=2~cost.length+1){递归公式}
④最后返回数组中最后一个值即dp[dp.length-1]
⑤因为此题中cost的长度>=2,所以不用特殊情况处理,在初始化和下面循环的时候,不会越界。
下面为代码(java):
5.总结:
①动态规划入门,有5步曲,按5步分析可以贯彻整个dp篇。
②斐波那契数题目中要注意的就是,容易造成索引越界,所以在初始的时候要判断特殊的情况,防止越界。
③爬楼梯本质上就是斐波那契,也要注意越界问题,第三题中cost的长度保证了不会越界。