问题描述
小U挑战一座非常高的楼梯。每次可以选择走一步或两步,但有一个限制:不能连续走两步(即一次跨两级台阶之后,下一步不能再跨两级)。给定楼梯的层数 nn,求小U有多少种不同的方式可以从底部走到顶端。
测试样例
样例1:
输入:
n = 2
输出:2
样例2:
输入:
n = 3
输出:3
样例3:
输入:
n = 4
输出:4
问题分析
本问题是一个动态规划问题。传统楼梯问题的变种通常允许连续选择步数,但在此约束下,需要考虑每次步数的不同组合和合法性。
动态规划定义
-
状态定义: 用 dp[i][j] 表示走到第 i 级台阶时,最后一步是 j 步的方法数:
- dp[i][1]:最后一步是 1 步。
- dp[i][2]:最后一步是 2 步。
状态转移方程
要走上第 i 级台阶,只有两种转移方式:
-
最后一步是 1 步:
- 上一步可以是 1 步或者 2 步:
dp[i][1]=dp[i−1][1]+dp[i−1][2]
-
最后一步是 2 步:
- 上一步只能是 1 步(因为不能连续两次跨两级):
dp[i][2]=dp[i−2][1]
初始条件
-
当 i=1(第一层台阶):
- dp[1][1]=1dp[1][1] = 1(只能走一步到达)。
- dp[1][2]=0dp[1][2] = 0(不可能直接跨到第一层)。
-
当 i=2(第二层台阶):
- dp[2][1]=1dp[2][1] = 1(从第一层走一步到第二层)。
- dp[2][2]=1dp[2][2] = 1(从第 0 层跨两级到第二层)。
最终结果
总的走法数是:dp[n][1]+dp[n][2]
代码实现
package main
import (
"fmt"
)
func solution(n int) int {
if n == 1 {
return 1
} else if n == 2 {
return 2
}
//创建dp表
dp := make([][3]int, n+1)
//初始条件
dp[1][1], dp[1][2] = 1, 0
dp[2][1], dp[2][2] = 1, 1
//状态转移
for i := 3; i <= n; i++ {
dp[i][1] = dp[i-1][1] + dp[i-1][2]
dp[i][2] = dp[i-2][1]
}
//返回结果
return dp[n][1] + dp[n][2]
}
func main() {
// Add your test cases here
fmt.Println(solution(2) == 2)
fmt.Println(solution(5) == 6)
fmt.Println(solution(6) == 9)
}
时间和空间复杂度分析
-
时间复杂度:
动态规划需要计算从 1 到 n 的所有台阶状态,每次转移是常数时间 O(1)。总时间复杂度为O(n) -
空间复杂度:
动态规划表格的大小为 (n+1)×3,空间复杂度为O(n)
优化方案
观察到每一层的状态仅依赖于上一层(或前两层),因此我们可以用常数变量存储状态,减少空间消耗,将空间复杂度优化到O(1)。