第九章 动态规划 Part 01
今天正式开始 动态规划 的学习!
理论基础
无论大家之前对动态规划的理解达到什么程度,建议都要先看一下我讲的 动态规划理论基础。这是整个动态规划学习的基石。
如果你之前没有做过动态规划的题目,读到这里可能会感觉内容简单,但其实在每道动态规划题目中都可以应用到这些基础理论,所以理解基础非常重要!
动态规划中每一个状态一定是由上一个状态推导出来的,
如下五步曲,这五步都搞清楚了,才能说把动态规划真的掌握了!
- 确定dp数组(dp table)以及下标的含义
- 确定递推公式
- dp数组如何初始化
- 确定遍历顺序
- 举例推导dp数组
debug的最好方式就是把dp数组打印出来. 知道这些,直接开干。个人感觉B站视频没必要看,看下文章即可。
509. 斐波那契数
这道题是动态规划的入门题目,虽然简单,但它是理解 动态规划五部曲 的关键。通过这道题可以很好地掌握动态规划的基础方法。
class Solution {
public:
int fib(int n) {
if(n<=1)
return n;
else
return fib(n-1)+fib(n-2);
}
};
遇到的最简单的题目,一个递归就解决了,连bug都没有改。
不过也是学习下,简单题目是用来加深对解题方法论的理解的。
-
确定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];中可以看出,dp[i]是依赖 dp[i - 1] 和 dp[i - 2],那么遍历的顺序一定是从前到后遍历的 -
举例推导dp数组
按照这个递推公式dp[i] = dp[i - 1] + dp[i - 2],我们来推导一下,当N为10的时候,dp数组应该是如下的数列:
0 1 1 2 3 5 8 13 21 34 55
确实,思路非常清晰。用五步法,按照顺序学,先给点甜头尝尝。
如果代码写出来,发现结果不对,就把dp数组打印出来看看和我们推导的数列是不是一致的。
70. 爬楼梯
这道题大家可以先自己思考一下,之后你会发现它和 斐波那契数 问题有一定的联系。解决这类问题的方法可以帮助你理解如何使用动态规划来优化解决问题的效率。
这题看到和 斐波那契数 问题有一定的联系,直接就联想到,爬n层楼梯只要爬n-1层楼梯再爬一层,或者只要爬n-2层楼梯再爬两层,好家伙,不就是找规律,难得还是用公式把规律展现出来。我最开始写的是斐波那契数,但是超时了。还是得靠遍历, 遍历以后,空间复杂度,和时间复杂度大幅度下降。
class Solution {
public:
int climbStairs(int n) {
if(n<=2)
return n;
int sum1=1;
int sum2=2;
int temp;
for(int i=3;i<=n;i++)
{
temp=sum1;
sum1=sum2;
sum2+=temp;
}
return sum2;
}
};
746. 使用最小花费爬楼梯
这道题目在 力扣 上改过题目描述,新的描述更加清晰,明确指出第一步不需要花费。这让题目解法更加明确了。果然这题的第一步就是要去读题:给你一个整数数组 cost ,其中 cost[i] 是从楼梯第 i 个台阶向上爬需要支付的费用。一旦你支付此费用,即可选择向上爬一个或者两个台阶。所以初始化 dp[0] = 0,dp[1] = 0;这一步还挺坑人的,注意一下,明白为什么即可
改后的题目相当于我文章中的 拓展 解法。
可以有两个途径得到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 - 1]跳还是从dp[i - 2]跳呢?
一定是选最小的,所以dp[i] = min(dp[i - 1] + cost[i - 1], dp[i - 2] + cost[i - 2]);
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[cost.size() ];
}
};
整体上题目还是比较简单的,核心就是找规律,只要找到规律就不难。