题目链接:
面试题 08.01. 三步问题 - 力扣(LeetCode)https://leetcode.cn/problems/three-steps-problem-lcci/description/
一、题目解析
题目:
题目讲解:
我们先举例查看规律:
第一台阶:我们从0台阶上到第一台阶,只需一步,就可以上去,所以只有一种上法
第二台阶:我们既可以从0台阶两步上到第二台阶,也可以先一步上第一台阶,再一步上到第二台阶,所以有两种
第三台阶:我们首先可以从0台阶三部上到第二台阶,其次可以先两步上第二台阶,再一步上第三台阶,同样也可以先一步上到第一台阶,再两步上到第二台阶,最后是一步一步上到第三台阶,总共有四种上去的方法。
那这里有没有什么规律呢?
- 我们已经知道上第一台阶只有一种方法,上第一台阶后我们可以再跨两步上到第三台阶,这便成为了上第三台阶的一种方式!
- 同样,我们知道上第二台阶有两种方法,这两种方法再跨一步就可以上到第三台阶,这不就成为了上第三台阶的两种方式了嘛
- 第四种方法是从0台阶跨三步,这里没有明显的规律,我们接着看第四台阶
第四台阶:根据我们从第三台阶发现的一点规律,我们在这里观察一下
首先,我们要清楚,我们最高只能走三步,也就是想走三步我们只能从第一台阶走
- 我们已经知道上第一台阶有一种方法,那上到第一台阶后再跨三部到第四台阶,就是我们上第四台阶的一种方式。
- 同样,我们知道上第二台阶有两种方法,这两种方法再跨两步可以上到第四台阶,这不就成为了我们上第四台阶的方式了嘛。
- 最后,上第三台阶有三种方法,那这三种方法再跨一步可以上到第四台阶,就是我们上到第四台阶的四种方式。
我们可以发现,上第四台阶的方法是上前面三个台阶的方法之和:1+2+4=7
综上,我们已经发现规律:从第四台阶开始,上每一台阶的方法是上前面三个台阶的方法之和
二、算法原理
1、状态表示
我们在状态标识的时候,一般都会创建一个数组dp,也就是我们所说的dp表,我们要做的就是把每一个状态的值填入这个表内,最终这个表内的某一个值可能就是我们要返回的值。
状态简单理解就是dp表内某一个值代表的含义。
我们通常选择以一个位置为开始为开始或者为结尾
如何确定状态表示
- 题目要求
简单的题目里一般会给出
- 经验+题目要求
越学越深入,动态规划也是熟能生巧,在题目中没有明显给出的时候,我们就要凭借自己做题的经验来确定,所以就需要我们大量的做题。
- 分析问题的过程中,发现重复子问题
分析问题的过程中把重复子问题抽象成我们的状态表示,这个更难理解,一切的基础都是我们先对动态规划算法熟练运用。我也不懂,我们慢慢来。
那我们这道题的状态表示应该是什么呢?
根据我们对题目的了解,题目让我们求上到第n台阶的方法有多少,那我们创建一个数组dp,让dp[n]表示我们到达第n台阶的方法有多少。
2、状态转移方程
确定状态表示之后我们就可以根据状态标识推出状态转移方程
状态转移方程是什么?
不讲什么复杂的,简单来说状态转移方程就是 dp[i]等于什么 dp[i]=?
这个就是状态转移方程,我们要做的,就是推出dp[i]等于什么
我们根据状态表示再结合题目+经验去推理转移方程,这一步也是我们整个解题过程中最难的一步
我们在这道题先简单了解下什么是状态转移方程,之后比较难的题目再细推
根据我们在题目解析中发现的规律:从第四台阶开始,上每一台阶的方法是上前面三个台阶的方法之和
我们可知状态转移方程为:dp[i]=dp[i-1]+dp[i-2]+dp[i-3]
3、初始化
我们创建dp表就是为了把他填满,我们初始化是为了防止在填表的过程中越界或者其它小的错误
怎么谈越界?
当i等于1时,dp[i-2],dp[i-3]都是越界,根本没有这个台阶,所以我们要进行初始化。
我们这道题也可以知道,规律是从第四台阶开始的,所以我们初始化前三台阶,是为了后面更好的进行
初始化:dp[1]=1 dp[2]=2 dp[3]=4
注:这里的下标没有错,我们为了做题更好理解,再对数组dp进行定义时会比台阶数多1,这样下标就与台阶对应,更好的理解!
4、填表顺序
注意填表顺序,是因为我们需要在填当前状态的时候,所需要的状态已经计算过了
这个意思就是,我们在填状态dp[4]的时候,我们就已经知道其所需要的dp[1]、dp[2]、dp[3]状态的值了, 那假如我们直接填dp[4],可其所需要的状态dp[3]我们还没填,所以就计算不出来我们当前状态dp[4]的值,所以填表顺序也是需要考虑的一项
这道题的填表顺序就是我们需要从状态dp[4]开始,依次填表。
5、返回值
返回值就是我们最后需要求出的值,在这里也就是我们我们的数组dp的最后一个数dp[n]。返回值一般通过题目要求和状态表示来判断出来。
以上就是我们算法原理的五步,这五步完成,其实我们就已经可以解题了。
三、编写代码
class Solution {
public:
int waysToStep(int n) {
//细节问题
const int Mod=1e9+7;
if(n==1||n==2)
{
return n;
}
if(n==3)
{
return 4;
}
//1、创建dp表
vector<int> dp(n+1);
//2、初始化
dp[1]=1;dp[2]=2;dp[3]=4;
//3、填表
for(int i=4;i<=n;i++)
{
dp[i]=((dp[i-1]+dp[i-2])%Mod+dp[i-3])%Mod;
}
//4、返回值
return dp[n];
}
};
问题解释:
- 细节1:我们需要模上1e9+7,否则数太大会被不通过,注意模的细节,两个的和先模,模完与另外一个数相加继续再模一次.
- 细节2:因为第一、二、三台阶没有规律,不能进入填表,所以先检查台阶数,如果是一、二、三台阶,那么就返回其对应的方法数即可