步骤:
- 确定dp数组(dp table)以及下标的含义
- 确定递推公式
- dp数组如何初始化
- 确定遍历顺序
- 举例推导dp数组
题目一. 斐波那契数列
1. 确定dp数组以及下标的含义
dp[i]的定义为:第i个数的斐波那契数值是dp[i]
2. 确定递推公式
状态转移方程 dp[i] = dp[i - 1] + dp[i - 2];
3. dp数组如何初始化
dp[0] = 0;
dp[1] = 1;
4. 确定遍历顺序
从递归公式dp[i] = dp[i - 1] + dp[i - 2];中可以看出,dp[i]是依赖 dp[i - 1] 和 dp[i - 2],那么遍历的顺序一定是从前到后遍历的
5. 举例推导dp数组
按照这个递推公式dp[i] = dp[i - 1] + dp[i - 2],我们来推导一下,当N为10的时候,dp数组应该是如下的数列:
0 1 1 2 3 5 8 13 21 34 55
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 index = 2; index <= n; index++){
dp[index] = dp[index - 1] + dp[index - 2];
}
return dp[n];
}
}
使用记录的变量来迭代,空间复杂度O(1)
class Solution {
public int fib(int n) {
if (n <= 1) return n;
int a = 0, b = 1, c = 0;
for (int i = 1; i < n; i++){
c = a + b;
a = b;
b = c;
}
return c;
}
}
2.爬楼梯
1.确定dp数组以及下标的含义
dp[i]: 爬到第i层楼梯,有dp[i]种方法
2.确定递推公式
如何可以推出dp[i]呢?
从dp[i]的定义可以看出,dp[i] 可以有两个方向推出来。
首先是dp[i - 1],上i-1层楼梯,有dp[i - 1]种方法,那么再一步跳一个台阶不就是dp[i]了么。
还有就是dp[i - 2],上i-2层楼梯,有dp[i - 2]种方法,那么再一步跳两个台阶不就是dp[i]了么。
那么dp[i]就是 dp[i - 1]与dp[i - 2]之和!所以dp[i] = dp[i - 1] + dp[i - 2] 。
在推导dp[i]的时候,一定要时刻想着dp[i]的定义,否则容易跑偏。这体现出确定dp数组以及下标的含义的重要性!
3.dp数组如何初始化
需要注意的是:题目中说了n是一个正整数,题目根本就没说n有为0的情况。所以本题其实就不应该讨论dp[0]的初始化!
所以不考虑dp[0]如何初始化,只初始化dp[1] = 1,dp[2] = 2,然后从i = 3开始递推,这样才符合dp[i]的定义。
4.确定遍历顺序
从递推公式dp[i] = dp[i - 1] + dp[i - 2];中可以看出,遍历顺序一定是从前向后遍历的
5.举例推导dp数组
举例当n为5的时候,dp table(dp数组)应该是这样的
class Solution {
public int climbStairs(int n) {
int[] dp = new int[n + 1];
dp[0] = 1;
dp[1] = 1;
for (int i = 2; i <= n; i++) {
dp[i] = dp[i - 1] + dp[i - 2];
}
return dp[n];
}
}
爬楼梯步数可以为1,2...m:
转化为完全背包的排列问题
class Solution {
public int climbStairs(int n) {
int[] dp = new int[n + 1];
int[] weight = {1,2};
dp[0] = 1;
for (int i = 0; i <= n; i++) {
for (int j = 0; j < weight.length; j++) {
if (i >= weight[j]) dp[i] += dp[i - weight[j]];
}
}
return dp[n];
}
}
3.