每日一解 最简单的动归思路的问题 爬楼梯

题目 爬楼梯

假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
注意:给定 n 是一个正整数。

示例 1:
输入: 2
输出: 2
解释: 有两种方法可以爬到楼顶。

  1. 1 阶 + 1 阶
  2. 2 阶

示例 2:
输入: 3
输出: 3
解释: 有三种方法可以爬到楼顶。

  1. 1 阶 + 1 阶 + 1 阶
  2. 1 阶 + 2 阶
  3. 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)=5 1[(21+5 )n(215 )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))

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值