题目 爬楼梯
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
注意:给定 n 是一个正整数。
示例 1:
输入: 2
输出: 2
解释: 有两种方法可以爬到楼顶。
- 1 阶 + 1 阶
- 2 阶
示例 2:
输入: 3
输出: 3
解释: 有三种方法可以爬到楼顶。
- 1 阶 + 1 阶 + 1 阶
- 1 阶 + 2 阶
- 2 阶 + 1 阶
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/climbing-stairs
思路
爬楼梯这个问题这应该也是我第三次遇到了吧,或者说至少是第三次做这个问题了。这个问题乍看起来好像是个不太轻松的动态规划问题,但是我第一次碰到这个问题的时候根本不用动归,用递归就可以完成这个问题了,原因很简单:
我们假设只有1个台阶,那么答案只有1种。
我们假设有2个台阶,那么答案有2种。
我们假设有3个台阶,那么答案有3种。
假设是4个台阶呢?答案是:1,1,1,1和2,1,1和1,2,1和1,1,2和2,2一共5种。
其实此时已经可以看出些端倪了。没错公式是:
f(x) = f(x-1) + f(x-2)
简直不要更简单,本质就是个斐波那契数列的递推公式。那么不难拿出一个递推的代码:
class Solution {
public:
int climbStairs(int n) {
if (n == 1) {
return 1;
}
if (n == 2) {
return 2;
}
return climbStairs(n - 1) + climbStairs(n - 2);
}
};
果不其然的超时了。
所以还是用动归优化一下时间比较好。毕竟刚才其实已经算是得到了状态转移方程。那么也就是改成非递归的循环来解决一下就好,这样就不会像递归一样经历很多不必要的循环:
class Solution {
public:
int climbStairs(int n) {
int a = 1, b = 2;
if (n == 1) {
return a;
}
if (n == 2) {
return b;
}
int ans;
for (int i = 3; i <= n; i++) {
ans = a + b;
a = b;
b = ans;
}
return ans;
}
};
但是有没有更优的办法呢?因为这个问题实在是和斐波那契数列问题几乎一模一样,所以我们不妨把斐波那契数列的通项公式拿过来一用:
f ( n ) = 1 5 [ ( 1 + 5 2 ) n − ( 1 − 5 2 ) n ] f(n)=\frac{1}{\sqrt{5}}\left[\left(\frac{1+\sqrt{5}}{2}\right)^{n}-\left(\frac{1-\sqrt{5}}{2}\right)^{n}\right] f(n)=51[(21+5)n−(21−5)n]
然后直接照着公式编码就好,我们的代码为:
class Solution {
public:
int climbStairs(int n) {
n++;
int answer = (1.0 / sqrt(5) * (pow((1.0 + sqrt(5)) / 2.0, n) - pow((1.0 - sqrt(5)) / 2.0, n)));
return answer;
}
};
(其实这样代码只是比较短而已,时间复杂度方面因为pow()函数的缘故,依然是log(n))