● 理论基础
五步走:
确定dp数组
递推公式
dp数组初始化
遍历顺序
打印dp数组
● 509. 斐波那契数
这道题比较简单,但自己不看答案还做不出来,没想到用初始的方法
先看递归 只需要两行代码 确定终止条件 和逻辑
class Solution {
public:
int fib(int n) {
//递归
if(n<2) return n;
return fib(n-1)+fib(n-2);
}
};
接下来看动态规划五部曲:
class Solution {
public:
int fib(int n) {
if(n == 0) return 0;
if(n == 1) return 1;
//初始化dp数组
vector<int> dp(n+1);
//dp数组初始化
dp[0]=0;
dp[1]=1;
//遍历顺序从前向后
for(int i = 2;i<=n;i++){
//递推公式
dp[i] = dp[i-1] + dp[i-2];
}
return dp[n];
}
};
● 70. 爬楼梯
这道题难点在递推公式 分析之后发现这道题和斐波那契数列的递推公式一摸一样
确定dp数组以及下标的含义
dp[i]: 爬到第i层楼梯,有dp[i]种方法
确定递推公式
如何可以推出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数组以及下标的含义的重要性!
dp数组如何初始化
再回顾一下dp[i]的定义:爬到第i层楼梯,有dp[i]种方法。
那么i为0,dp[i]应该是多少呢,这个可以有很多解释,但基本都是直接奔着答案去解释的。
例如强行安慰自己爬到第0层,也有一种方法,什么都不做也就是一种方法即:dp[0] = 1,相当于直接站在楼顶。
但总有点牵强的成分。
那还这么理解呢:我就认为跑到第0层,方法就是0啊,一步只能走一个台阶或者两个台阶,然而楼层是0,直接站楼顶上了,就是不用方法,dp[0]就应该是0.
其实这么争论下去没有意义,大部分解释说dp[0]应该为1的理由其实是因为dp[0]=1的话在递推的过程中i从2开始遍历本题就能过,然后就往结果上靠去解释dp[0] = 1。
从dp数组定义的角度上来说,dp[0] = 0 也能说得通。
需要注意的是:题目中说了n是一个正整数,题目根本就没说n有为0的情况。
所以本题其实就不应该讨论dp[0]的初始化!
我相信dp[1] = 1,dp[2] = 2,这个初始化大家应该都没有争议的。
所以我的原则是:不考虑dp[0]如何初始化,只初始化dp[1] = 1,dp[2] = 2,然后从i = 3开始递推,这样才符合dp[i]的定义。
确定遍历顺序
从递推公式dp[i] = dp[i - 1] + dp[i - 2];中可以看出,遍历顺序一定是从前向后遍历的
举例推导dp数组
举例当n为5的时候,dp table(dp数组)应该是这样的
如果代码出问题了,就把dp table 打印出来,看看究竟是不是和自己推导的一样。
此时大家应该发现了,这不就是斐波那契数列么!
唯一的区别是,没有讨论dp[0]应该是什么,因为dp[0]在本题没有意义!
class Solution {
public:
int climbStairs(int n) {
if(n<2) return n;
vector<int> dp(n+1);
dp[1]=1;
dp[2]=2;
for(int i = 3;i<=n;i++){
dp[i] = dp[i-1] +dp[i-2];
}
return dp[n];
}
};
● 746. 使用最小花费爬楼梯
还是五部曲
明确dp[i]含义 表示到达第i个台阶需要的最少花费
递推公式 与i-1和i-2有关系 还有他们的cost
初始化,到达第0个台阶和第一个台阶的最小花费为0
遍历顺序
打印dp数组
class Solution {
public:
int minCostClimbingStairs(vector<int>& cost) {
vector<int> dp(cost.size()+1);
dp[0] = 0;
dp[1] = 0;
for(int i = 2;i<=cost.size();i++){
dp[i] = min(dp[i-1]+cost[i-1],dp[i-2]+cost[i-2]);
}
return dp.back();
}
};