题目链接:
题目描述:
#面试题 08.01. 三步问题
三步问题。有个小孩正在上楼梯,楼梯有n阶台阶,小孩一次可以上1阶、2阶或3阶。实现一种方法,计算小孩有多少种上楼梯的方式。结果可能很大,你需要对结果模1000000007。
示例1:
输入:n = 3
输出:4
说明: 有四种走法
示例2:
输入:n = 5
输出:13
提示:
n范围在[1, 1000000]之间
通过次数7,771 | 提交次数22,802
分析:
1.这个题是爬楼梯问题的变形,关于经典的 “爬楼梯” 问题,动态规划的入门题,文章链接如下:
题目本身难度不大,满足动态规划思想求解的条件。设 dp[i] 为到达第 i 个台阶的方法数,最后一步可以选择上一阶、两阶、三阶:dp[i] = dp[i - 1] + dp[i - 2] + dp[i - 3]。
初始化:
dp[0] = 1(如果 dp 为0,代表无法到达,故此处应为 1);
dp[1] = 1;
dp[2] = 2。
2.这个题最大的坑,在于题目中的这句话:结果可能很大,你需要对结果模1000000007。
这是笔试题中很常见的一种题型,从题目来看,n 的范围在 [1, 1000000] 之间,我们测一下 n 为最大值,结果是负数。
这个原因是 dp 的值可能会超过 int 的范围(大约在 n=42 时),一旦超出范围(溢出),数值就不再准确,因为改变了最高位,所以可能出现变为负数的情况。
为了避免超过 int 范围,我们需要进行取模运算(此处代码用的是取余运算,这两者不完全相同,但是本题不会涉及到两者的区别,感兴趣可以自行查阅资料,),dp[i] = dp[i - 1] + dp[i - 2] + dp[i - 3],其中任何一个加法都可能导致超过 int 的范围,故应在每步都做取模运算。
3.动态规划在空间复杂度上的优化老套路:每次计算 dp[i] 只会用到前面三个的数据,不必创建一维数组 dp[]。
代码:
先上提交截图:
优化后代码如下:
/*
*动态规划,设 dp[i] 为到达第 i 个台阶的方法数
*dp[i] = dp[i - 1] + dp[i - 2] + dp[i - 3],最后一步可以选择上一阶、两阶、三阶
*dp[0] = 1(如果 dp 为0,代表无法到达,故此处应为 1), dp[1] = 1, dp[2] = 2
*n范围在[1, 1000000]之间,也就是说 dp 很可能会超出 int 的范围
*一旦超出范围(溢出),数值就不再准确,变为负数
*优化:每次计算 dp[i] 只会用到前面三个的内容,不必创建一维数组
*/
class Solution {
public int waysToStep(int n) {
if(n < 3) return n;
int dp = 0;
int prev1 = 1; //初始化
int prev2 = 1;
int prev3 = 2;
for(int i = 3; i <= n; i++){//为避免超出 int 范围,进行取模运算
dp = ((prev1 + prev2) % 1000000007 + prev3) % 1000000007;
prev1 = prev2;
prev2 = prev3;
prev3 = dp;
}
return dp;
}
}