今天开始动态规划了~
动态规划的解题步骤
- 确定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()];
}
};