适用范围:重叠子问题多
解题步骤:
1.确定dp数组以及下标含义
2.确定递推公式
3.dp数组如何初始化
4.确定遍历顺序
5.举例推导dp数组
做题时,写代码之前先把状态转移在dp数组上的情况模拟一遍,在心中有数,确定最后推出的是想要的结果。然后再写代码,代码没通过就打印dp数组,看看是不是和自己预先推导的结果一样。
斐波那契数列
动态规划五部曲:
1.确定dp数组以及下标的含义
dp[i]的定义是——第个数的斐波那契数列数值是dp[i]
2.确定递推公式
递推公式已给出dp[i]=d[i-1]+d[i+2];
3.dp数组如何初始化
dp[0] = 0;
dp[1] = 1;
4.确定遍历顺序
从递归公式可以知道dp[i]依赖于dp[i-1]和dp[i-2],那么递归的顺序一定是从前到后。
class Solution {
public:
int fib(int N) {
if (N <= 1) return N;
vector<int> dp(N + 1);
dp[0] = 0;
dp[1] = 1;
for (int i = 2; i <= N; i++) {
dp[i] = dp[i - 1] + dp[i - 2];
}
return dp[N];
}
};
递归解法:
class Solution {
public:
int fib(int N) {
if (N < 2) return N;
return fib(N - 1) + fib(N - 2);
}
};
爬楼梯
1.dp[i]表示爬到第i层,有dp[i]种方法
2.dp[i]=dp[i-1]+dp[i-2]
首先是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] 。
(就好比A到B,有个路口,往左走有x种方法到B,往右有y种方法,那么从A到B就有x+y种方法)
3.初始化
dp[1]=1;
dp[2]=2;
4.确定遍历顺序
从前往后
5.举例推导
当n等于一个具体数值,尝试手动打印一下
代码:
class Solution {
public:
int climbStairs(int n) {
if (n <= 1) return n; // 因为下面直接对dp[2]操作了,防止空指针
vector<int> dp(n + 1);
dp[1] = 1;
dp[2] = 2;
for (int i = 3; i <= n; i++) { // 注意i是从3开始的
dp[i] = dp[i - 1] + dp[i - 2];
}
return dp[n];
}
};
优化:
class Solution {
public:
int climbStairs(int n) {
if (n <= 1) return n;
int dp[3];
dp[1] = 1;
dp[2] = 2;
for (int i = 3; i <= n; i++) {
int sum = dp[1] + dp[2];
dp[1] = dp[2];
dp[2] = sum;
}
return dp[2];//结果放进dp[2]
}
};
最小花费爬楼梯