leetcode-master/problems/周总结/20210107动规周末总结.md

153 lines
6.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

这周我们正式开始动态规划的学习!
## 周一
在[关于动态规划,你该了解这些!](https://mp.weixin.qq.com/s/ocZwfPlCWrJtVGACqFNAag)中我们讲解了动态规划的基础知识。
首先讲一下动规和贪心的区别,其实大家不用太强调理论上的区别,做做题,就感受出来了。
然后我们讲了动规的五部曲:
1. 确定dp数组dp table以及下标的含义
2. 确定递推公式
3. dp数组如何初始化
4. 确定遍历顺序
5. 举例推导dp数组
后序我们在讲解动规的题目时候,都离不开这五步!
本周都是简单题目,大家可能会感觉 按照这五部来好麻烦,凭感觉随手一写,直接就过,越到后面越会感觉,凭感觉这个事还是不靠谱的,哈哈。
最后我们讲了动态规划题目应该如何debug相信一些录友做动规的题目一旦报错也是凭感觉来改。
其实只要把dp数组打印出来哪里有问题一目了然
**如果代码写出来了一直AC不了灵魂三问**
1. 这道题目我举例推导状态转移公式了么?
2. 我打印dp数组的日志了么
3. 打印出来了dp数组和我想的一样么
哈哈专治各种代码写出来了但AC不了的疑难杂症。
## 周二
这道题目[动态规划:斐波那契数](https://mp.weixin.qq.com/s/ko0zLJplF7n_4TysnPOa_w)是当之无愧的动规入门题。
简单题我们就是用来了解方法论的用动规五部曲走一遍题目其实已经把递推公式和dp数组如何初始化都给我们了。
## 周三
[动态规划:爬楼梯](https://mp.weixin.qq.com/s/Ohop0jApSII9xxOMiFhGIw) 这道题目其实就是斐波那契数列。
但正常思考过程应该是推导完递推公式之后,发现这是斐波那契,而不是上来就知道这是斐波那契。
在这道题目的第三步确认dp数组如何初始化其实就可以看出来对dp[i]定义理解的深度。
dp[0]其实就是一个无意义的存在不用去初始化dp[0]。
有的题解是把dp[0]初始化为1然后遍历的时候i从2开始遍历这样是可以解题的然后强行解释一波dp[0]应该等于1的含义。
一个严谨的思考过程应该是初始化dp[1] = 1dp[2] = 2然后i从3开始遍历代码如下
```CPP
dp[1] = 1;
dp[2] = 2;
for (int i = 3; i <= n; i++) { // 注意i是从3开始的
dp[i] = dp[i - 1] + dp[i - 2];
}
```
这个可以是面试的一个小问题哈哈考察候选人对dp[i]定义的理解程度。
这道题目还可以继续深化,就是一步一个台阶,两个台阶,三个台阶,直到 m个台阶有多少种方法爬到n阶楼顶。
这又有难度了,这其实是一个完全背包问题,但力扣上没有这种题目,所以后续我在讲解背包问题的时候,今天这道题还会拿从背包问题的角度上来再讲一遍。
这里我先给出我的实现代码:
```CPP
class Solution {
public:
int climbStairs(int n) {
vector<int> dp(n + 1, 0);
dp[0] = 1;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) { // 把m换成2就可以AC爬楼梯这道题
if (i - j >= 0) dp[i] += dp[i - j];
}
}
return dp[n];
}
};
```
代码中m表示最多可以爬m个台阶。
**以上代码不能运行哈我主要是为了体现只要把m换成2粘过去就可以AC爬楼梯这道题不信你就粘一下试试哈哈**
**此时我就发现一个绝佳的大厂面试题**第一道题就是单纯的爬楼梯然后看候选人的代码实现如果把dp[0]的定义成1了就可以发难了为什么dp[0]一定要初始化为1此时可能候选人就要强行给dp[0]应该是1找各种理由。那这就是一个考察点了对dp[i]的定义理解的不深入。
然后可以继续发难,如果一步一个台阶,两个台阶,三个台阶,直到 m个台阶有多少种方法爬到n阶楼顶。这道题目leetcode上并没有原题绝对是考察候选人算法能力的绝佳好题。
这一连套问下来,候选人算法能力如何,面试官心里就有数了。
**其实大厂面试最喜欢问题的就是这种简单题,然后慢慢变化,在小细节上考察候选人**
这道绝佳的面试题我没有用过,如果录友们有面试别人的需求,就把这个套路拿去吧,哈哈哈。
我在[通过一道面试题目,讲一讲递归算法的时间复杂度!](https://mp.weixin.qq.com/s/I6ZXFbw09NR31F5CJR_geQ)中以我自己面试别人的真实经历通过求x的n次方 这么简单的题目,就可以考察候选人对算法性能以及递归的理解深度,录友们可以看看,绝对有收获!
## 周四
这道题目[动态规划:使用最小花费爬楼梯](https://mp.weixin.qq.com/s/djZB9gkyLFAKcQcSvKDorA)就是在爬台阶的基础上加了一个花费,
这道题描述也确实有点魔幻。
题目描述为:每当你爬上一个阶梯你都要花费对应的体力值,一旦支付了相应的体力值,你就可以选择向上爬一个阶梯或者爬两个阶梯。
示例1
输入cost = [10, 15, 20]
输出15
**从题目描述可以看出:要不是第一步不需要花费体力,要不就是第最后一步不需要花费体力,我个人理解:题意说的其实是第一步是要支付费用的!**。因为是当你爬上一个台阶就要花费对应的体力值!
所以我定义的dp[i]意思是也是第一步是要花费体力的,最后一步不用花费体力了,因为已经支付了。
之后一些录友在留言区说 可以定义dp[i]为:第一步是不花费体力,最后一步是花费体力的。
所以代码也可以这么写:
```CPP
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()];
}
};
```
这么写看上去比较顺,但是就是感觉和题目描述的不太符。哈哈,也没有必要这么细扣题意了,大家只要知道,题目的意思反正就是要不是第一步不花费,要不是最后一步不花费,都可以。
## 总结
本周题目简单一些,也非常合适初学者来练练手。
下周开始上难度了哈,然后大下周就开始讲解背包问题,好戏还在后面,录友们跟上哈。
学算法认准「代码随想录」就够了Carl带你打怪升级
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>