有限制的楼梯攀登
题目背景
小U爬楼梯的问题是经典的动态规划问题的一种变体。常规的楼梯问题通常是可以选择走一步或者两步,但这里加入了限制条件:不能连续两次走两步。这为问题增加了一定的复杂性。
核心问题
- 在有该限制的条件下,如何构造有效的递推关系?
- 如何通过动态规划的方式计算不同的走法?
解决思路
本题主要分为以下几步:
-
确定状态变量:
- 用动态规划方法来解决问题。
- 定义两个数组:
dp0[i]:表示从第 0 层到第 (i) 层,最后一步是走 1 步的走法数量。dp1[i]:表示从第 0 层到第 (i) 层,最后一步是走 2 步的走法数量。
-
递推关系:
- 如果当前层是通过 走 1 步 到达,那么上一层可以是走 1 步或者走 2 步:
- 如果当前层是通过 走 2 步 到达,那么再往前只能是走 1 步:
- 如果当前层是通过 走 1 步 到达,那么上一层可以是走 1 步或者走 2 步:
-
边界条件:
- 初始情况下:
- (dp0[0] = 1):表示站在第 0 层时,有一种方法(即什么也不做)。
- (dp1[0] = 0):因为在第 0 层不能通过走两步直接到达。
- 初始情况下:
-
最终解:
- 总的方法数是最后一步为 1 步或 2 步的方案之和:
- 总的方法数是最后一步为 1 步或 2 步的方案之和:
代码解析
以下是代码的逐步解析:
package main
import "fmt"
func solution(n int) int {
// 如果楼梯为0级,直接返回1种方法
if n == 0 {
return 1
}
// 创建两个动态规划数组
dp0 := make([]int, n+1) // dp0[i]存储到达第i级楼梯,最后一步走1步的方法数
dp1 := make([]int, n+1) // dp1[i]存储到达第i级楼梯,最后一步走2步的方法数
// 初始条件
dp0[0] = 1 // 第0级楼梯,最后一步是走1步的情况
dp1[0] = 0 // 第0级楼梯,没有走2步的情况
// 如果楼梯至少有1级
if n >= 1 {
dp0[1] = 1 // 第1级楼梯,只能通过走1步到达
dp1[1] = 0 // 第1级楼梯,不能通过走2步到达
}
// 动态规划递推公式
for i := 2; i <= n; i++ {
// 最后一步是走1步
dp0[i] = dp0[i-1] + dp1[i-1]
// 最后一步是走2步
dp1[i] = dp0[i-2]
}
// 返回两种方式的总和
return dp0[n] + dp1[n]
}
func main() {
// 测试样例
fmt.Println(solution(2) == 2) // 输出: true
fmt.Println(solution(3) == 3) // 输出: true
fmt.Println(solution(4) == 4) // 输出: true
fmt.Println(solution(5) == 6) // 输出: true
fmt.Println(solution(0) == 1) // 输出: true
fmt.Println(solution(1) == 1) // 输出: true
}
代码细节解析
初始化
-
dp0[0]和dp1[0]初始化- (dp0[0] = 1),因为从 0 层到 0 层什么也不做是一种有效方案。
- (dp1[0] = 0),因为从 0 层到 0 层不能跨两步。
-
对于第一层
- (dp0[1] = 1),第一层只能通过走 1 步到达。
- (dp1[1] = 0),第一层无法通过走两步到达。
递推公式
-
(dp0[i] = dp0[i-1] + dp1[i-1])
- 当前层最后一步走 1 步到达,那么上一层的状态可以是走 1 步(dp0[i-1])或者走 2 步(dp1[i-1])。
-
(dp1[i] = dp0[i-2])
- 当前层最后一步走 2 步到达,那么只能是从 (i-2) 层通过走 1 步过来。
返回结果
- 最终的结果是 (dp0[n] + dp1[n]),即所有从底部到顶端的不同走法。
复杂度分析
- 时间复杂度:
- 动态规划遍历一遍楼梯,总体时间复杂度为 (O(n))。
- 空间复杂度:
- 需要两个数组存储中间状态,空间复杂度为 (O(n))。
个人思考与总结
-
动态规划的优势:
- 通过明确递推关系,可以有效避免重复计算。
- 本题中的递推关系,结合了状态转移的思想,既考虑了最后一步的步数,又保证了约束条件的正确性。
-
问题的变形与扩展:
- 如果约束条件变为不能连续走同样的步数,问题将如何变化?
- 如果允许更大的步数,比如 1、2 和 3 步,又该如何处理?
总结
本题通过动态规划的方式,将复杂的约束条件转化为简单的状态转移关系。代码逻辑清晰,递推公式易于理解,适合用来学习动态规划的基本思路。