题目序号:124
题目难度:中等
问题描述:
小U最近决定挑战一座非常高的楼梯,每次他可以选择走一步或两步,但有一个重要的限制:他不能连续走两步。因此,小U想知道他总共有多少种不同的方式可以从楼梯的底部走到顶端。
你需要帮他计算在给定的楼梯层数下,小U有多少种走法。
1.题目理解
这个问题是一个经典的动态规划问题,我们可以使用递归的方式来解决。我们定义一个函数 f(n) 来表示到达第 n 层楼梯的走法数量。根据题目描述,小U每次可以选择走一步或两步,但不能连续走两步。因此,我们可以得到以下递推关系。其中要着重理解 “不能连续走两步” 这个限制
(1)如果小U在第 n 层,他可以从第 n-1 层走一步上来,或者从第 n-2 层走两步上来。但是,如果他从第 n-2 层走两步上来,那么他不能从第 n-3 层走两步上来,因为这样就违反了不能连续走两步的规则。所以,我们有: f(n)=f(n−1)+f(n−3),即我们可以从上一个台阶选择走一步上来,也可以选择从上三个台阶选择走1步加2步或者2步加1步,这样就能够避免连续走两步。这也是本题最难的地方,不能使用简单的f(n) = f(n-1) + f(n-2)
(2)但是,当小U在第1层或第2层时,他不能从更低的楼层走两步上来,所以我们有:f(1)==1;f(2)==2;f(3)==3
2.代码
public class Main {
public static int solution(int n) {
// Edit your code here
int[] dp =new int[n+1];
if(n==1){
return 1;
}else if(n==2){
return 2;
}else if(n==3){
return 3;
}
else{
dp[0]=0;
dp[1]=1;
dp[2]=2;
dp[3]=3;
for(int i=4;i<=n;i++){
dp[i]=dp[i-1]+dp[i-3];
}
return dp[n];
}
}
public static void main(String[] args) {
// Add your test cases here
System.out.println(solution(2) == 2);
}
}
3.代码讲解
对于这道题目,只要理解了动态规划的状态转移方程起始代码就不难理解了,首先我们初始化一下dp数组,然后给第一层第二层和第三层都赋予初值,然后从第四层开始不断的执行状态转移方程dp[i]=dp[i-1]+dp[i-3];,并最终输出第n层的结果即可。
4.时间复杂度分析
本题只需要线性的遍历数组即可,所以时间复杂度显然为O(n),同理我们也能轻松的得出空间复杂度为O(n)。
5.相似例题
leetcode上面也有一道爬梯子的题目(第70题),只不过没有本题的限制条件,所以其实变得更加简单了。在本题中使用的动态规划方法在遇到较小的n时尚可,但是当n逐渐增大,O(n)也会显得捉襟见肘。那么我们看一下,如果不设不允许连续走两次两步这一限制,时间复杂度更小方法是什么。
在没有限制的情况下f(n) = f(n-1) + f(n-2),我们可以推导出如下矩阵,当我们看到矩阵的时候一定要想到快速幂算法(时间复杂度为O(logn))
具体代码如下
public class Solution {
public int climbStairs(int n) {
int[][] q = {{1, 1}, {1, 0}};
int[][] res = pow(q, n);
return res[0][0];
}
public int[][] pow(int[][] a, int n) {
int[][] ret = {{1, 0}, {0, 1}};
while (n > 0) {
if ((n & 1) == 1) {
ret = multiply(ret, a);
}
n >>= 1;
a = multiply(a, a);
}
return ret;
}
public int[][] multiply(int[][] a, int[][] b) {
int[][] c = new int[2][2];
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 2; j++) {
c[i][j] = a[i][0] * b[0][j] + a[i][1] * b[1][j];
}
}
return c;
}
}
快速幂的原理为:
将原先的幂次变为2的次方再相乘,就可以将O(n)转化为O(logn)