今天开始动态规划了~
动态规划的解题步骤
- 确定dp数组(dp table)以及下标的含义
- 确定递推公式
- dp数组如何初始化
- 确定遍历顺序
- 举例推导dp数组
斐波那契数
题目链接:力扣
由于题目给出了递归关系所以这题用递归法也很简单
int fib(int n) { //递归if(n<=1)return n;return fib(n-1)+fib(n-2);}
用动态规划:
- 确定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]的,所以遍历顺序为从前往后 - 举例推导
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];}
爬楼梯
题目链接:力扣
这题递推关系的推导,在b站评论里看到一个可以秒懂的:
在到达第n层的上一步,我们只有两个选择,走一步,或者走两步。
如果是走一步,我们需要先通过 f(n-1)种方式到达 n-1 层
如果是走两步, 我们需要通过 f(n-2)种方式到达第 n - 2 层
所以综上有 f(n) = f(n-2) + f(n-1)
这题,如果可以知道递推关系,就会发现和上一题差不多。
- 确定dp数组以及下标的含义
dp[i]: 爬到第i层楼梯,有dp[i]种方法 - 确定递推公式
见上述。 - dp数组如何初始化
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数组
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];}
};
使用最小花费爬楼梯
题目链接:力扣
- 确定dp数组以及下标的含义
dp[i]的定义:到达第i台阶的最少花费为dp[i]。 - 确定递推公式
可以有两个途径得到dp[i],一个是dp[i-1] 一个是dp[i-2]。
dp[i - 1] 跳到 dp[i] 需要花费 dp[i - 1] + cost[i - 1]。
dp[i - 2] 跳到 dp[i] 需要花费 dp[i - 2] + cost[i - 2]。
由于选择花费最少的,故dp[i] = min(dp[i - 1] + cost[i - 1], dp[i - 2] + cost[i - 2]); - dp数组如何初始化
根据dp数组的定义,到达第0台阶所花费的最小体力为dp[0],
同时,题目描述中明确说了 “你可以选择从下标为 0 或下标为 1 的台阶开始爬楼梯。”
也就是说 到达 第 0 个台阶是不花费的,但从 第0 个台阶 往上跳的话,需要花费 cost[0]。
所以初始化 dp[0] = 0,dp[1] = 0; - 确定遍历顺序
因为是模拟台阶,而且dp[i]由dp[i-1] dp[i-2]推出,所以是从前到后遍历cost数组就可以了。 - 举例推导dp数组
class Solution {
public:int minCostClimbingStairs(vector<int>& cost) {vector<int> dp(cost.size()+1); //dp[i]的定义:到达第i台阶所花费的最少体力为dp[i]。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]);cout<<"dp[" <<i <<"] = " << dp[i] <<endl;}return dp[cost.size()];}};