动态规划简介
动态规划,英文:Dynamic Programming,简称DP,如果某一问题有很多重叠子问题,使用动态规划是最有效的。
所以动态规划中每一个状态一定是由上一个状态推导出来的,这一点就区分于贪心,贪心没有状态推导,而是从局部直接选最优的。
动态规划五步曲
- 确定dp数组(dp table)以及下标的含义
- 确定递推公式
- dp数组如何初始化
- 确定遍历顺序
- 举例推导dp数组
注:一些情况是递推公式决定了dp数组要如何初始化!
动态规划应该如何debug
当我们去学动态规划的题很容易出现看完题解,自己上手写代码一直通过不了,首先我们要明白,写动态规划,代码出问题很正常!
debug:
把dp数组打印出来,看看究竟是不是自己的思路所推演的结果
写动态规划最忌讳的就是不清楚dp数组的含义,不懂为什么这样初始化,只会一味的去背代码,要明白,不是所有的题都能凭借所谓的题感直接写出来的,我们得去明白其深层得含义,理解自己为什么要这样去操作。
我们在去写代码之前,一定要先把状态转移到dp数组上进行具体得模拟一遍,做到心里有数
通过不了时,请给自己发出以下三个疑问:
这道题目我举例推导状态转移公式了么?
我打印dp数组的日志了么?
打印出来了dp数组和我想的一样么?
通过上述文字,想必我们对动态规划已经有了初步得了解,那我们就来试试下面这道题目把!
小题一道
题目
斐波那契数,通常用F(n)
表示,形成的序列称为 斐波那契数列 。该数列由0
和1
开始,后面的每一项数字都是前面两项数字的和。也就是: F(0) = 0,F(1) = 1 F(n) = F(n - 1) + F(n - 2)
,其中n > 1
给你n
,请计算F(n)
。
示例1
- 输入:2
- 输出:1
- 解释:F(2) = F(1) + F(0) = 1 + 0 = 1
示例2
- 输入:3
- 输出:2
- 解释:F(3) = F(2) + F(1) = 1 + 1 = 2
示例3
- 输入:4
- 输出:3
- 解释:F(4) = F(3) + F(2) = 2 + 1 = 3
思路
- 确定dp数组以及下标的含义
dp[i]的定义为:第i个数的斐波那契数值是dp[i] - 确定递推公式
状态转移方程 dp[i] = dp[i - 1] + dp[i - 2]; - dp数组如何初始化
题目已经告诉我们:dp[0]=0, dp[1]=1 - 确定遍历顺序
从递归公式dp[i] = dp[i - 1] + dp[i - 2];中可以看出,dp[i]是依赖 dp[i - 1] 和 dp[i - 2],那么遍历的顺序一定是从前到后遍历的 - 举例推导dp数组
按照这个递推公式dp[i] = dp[i - 1] + dp[i - 2],我们来推导一下,当N为8的时候,dp数组应该是如下的数列:0 1 1 2 3 5 8 13 21
注:如果代码写出来,发现结果不对,就把dp数组打印出来看看和我们推导的数列是不是一致的。
代码实现
完整dp解题代码
1 | class Solution { |
简化后的dp解题代码
1 | class Solution { |
递归解题代码
1 | public class Solution{ |