问题描述
小U最近决定挑战一座非常高的楼梯,每次他可以选择走一步或两步,但有一个重要的限制:他不能连续走两步。因此,小U想知道他总共有多少种不同的方式可以从楼梯的底部走到顶端。
你需要帮他计算在给定的楼梯层数下,小U有多少种走法。
测试样例
样例1:
输入:
n = 2
输出:2
样例2:
输入:
n = 3
输出:3
样例3:
输入:
n = 4
输出:4
思路
首先我们分析一个简化的情况:
1. 走1步的情况:
- 若楼梯的层数为 1 (n=1),小U只有一种选择,即走一步。
- 若楼梯的层数为 2 (n=2),小U有两种选择:走两步(2个1步),或者一次性走两步。
2. 走两步的情况:
-
若楼梯的层数大于2时,我们可以递推地分析问题。对于一个楼梯
n层,我们可以分为两种情况:- 如果小U的最后一步是走一步,那么他就必须从
n-1层的楼梯爬到顶。 - 如果小U的最后一步是走两步,那么他就必须从
n-2层的楼梯爬到顶。
- 如果小U的最后一步是走一步,那么他就必须从
因此,假设 dp[i] 表示从第0层爬到第i层的不同走法数,则有递推关系:
[ dp[i] = dp[i-1] + dp[i-2] ]
其中:
dp[i-1]代表最后一步是一步,从第i-1层到第i层。dp[i-2]代表最后一步是两步,从第i-2层到第i层。
初始条件:
dp[0] = 1(站在地面上)dp[1] = 1(爬到第一层只有一种方式,就是走一步)
动态规划实现
我们通过动态规划计算 dp 数组来得到最终结果。递推式的复杂度是 O(n),因此整个算法的时间复杂度是 O(n)。
代码
def solution(n):
# Edit your code here
if n == 1:
return 1
if n == 2:
return 2 # 到达第二层可以选择一步或两步
dp = [[0] * 2 for _ in range(n + 1)]
dp[0][0], dp[0][1] = 1, 0
dp[1][0], dp[1][1] = 1, 0
dp[2][0], dp[2][1] = 1, 1
for i in range(3, n + 1):
#0表示上一步走的是一步,1表示上一步走的是两步
dp[i][0] = dp[i - 1][0] + dp[i - 1][1] # 最后一步走了一步
dp[i][1] = dp[i - 2][0] # 最后一步走了两步
return dp[n][0] + dp[n][1] # 返回到达第 n 层的所有方式
if __name__ == "__main__":
# Add your test cases here
print(solution(2) == 2)
1. 基础情况处理
if n == 1:
return 1
if n == 2:
return 2 # 到达第二层可以选择一步或两步
- 当
n == 1时,表示楼梯只有一层,只有一种走法,就是走一步。 - 当
n == 2时,楼梯有两层,可以通过走一步两次,或者直接走两步,所以有两种走法。
这些基础情况对于递推过程非常重要,它们提供了边界条件。
2. 状态定义和初始化
dp = [[0] * 2 for _ in range(n + 1)]
dp[0][0], dp[0][1] = 1, 0
dp[1][0], dp[1][1] = 1, 0
dp[2][0], dp[2][1] = 1, 1
- 这里定义了一个二维数组
dp,其中dp[i][0]表示到达第i层时,最后一步是走一步的走法数;dp[i][1]表示到达第i层时,最后一步是走两步的走法数。 dp[0][0] = 1, dp[0][1] = 0:表示从地面到第0层(即已经在地面上)时,最后一步没有走,走法为 1(显然只有一种方式站在原地)。dp[1][0] = 1, dp[1][1] = 0:表示从第0层到第1层时,只有一步走法,所以dp[1][0] = 1,dp[1][1] = 0。dp[2][0] = 1, dp[2][1] = 1:表示到达第2层时,可以通过走一步从第1层到达,或者走两步直接从第0层到达。因此dp[2][0] = 1,dp[2][1] = 1。
3. 状态转移
for i in range(3, n + 1):
# 0表示上一步走的是一步,1表示上一步走的是两步
dp[i][0] = dp[i - 1][0] + dp[i - 1][1] # 最后一步走了一步
dp[i][1] = dp[i - 2][0] # 最后一步走了两步
dp[i][0] = dp[i - 1][0] + dp[i - 1][1]:表示到达第i层的最后一步走的是一步,可以从第i-1层通过走一步到达。dp[i-1][0]表示从第i-1层且上一步走的是一步,dp[i-1][1]表示从第i-1层且上一步走的是两步。总的来说,只要上一步走了 1 步或者 2 步,都可以从第i-1层到达第i层。dp[i][1] = dp[i - 2][0]:表示到达第i层的最后一步走了两步,只能从第i-2层通过走两步到达。此时,上一步走了一步的情况 (dp[i-2][0]) 是唯一的选择。
4. 返回结果
return dp[n][0] + dp[n][1] # 返回到达第 n 层的所有方式
- 最后返回到达第
n层的所有可能性,实际上是dp[n][0] + dp[n][1],因为最后一步可以是走一步或者走两步,二者的走法数之和即为总的走法数。
5. 时间复杂度分析
- 初始化二维数组
dp的时间复杂度是 O(n),因为数组的大小是(n+1) * 2。 - 填充
dp数组的时间复杂度是 O(n),因为你只需要进行一次循环,循环中每次操作都是常数时间的。 - 因此,整个算法的时间复杂度是 O(n)。
6. 空间复杂度分析
- 使用了一个
dp数组,大小为(n+1) * 2,因此空间复杂度是 O(n)。