青训营笔记9 | 豆包MarsCode AI刷题

13 阅读3分钟

问题描述

有限制的楼梯攀登

小U最近决定挑战一座非常高的楼梯,每次他可以选择走一步或两步,但有一个重要的限制:他不能连续走两步。因此,小U想知道他总共有多少种不同的方式可以从楼梯的底部走到顶端。

你需要帮他计算在给定的楼梯层数下,小U有多少种走法。

测试样例

样例1:

输入:n = 2
输出:2

样例2:

输入:n = 3
输出:3

样例3:

输入:n = 4
输出:4

思路分析

  1. 定义状态

    • 设 dp[i][0] 表示到达第 i 层时,上一步是走一步的走法数。
    • 设 dp[i][1] 表示到达第 i 层时,上一步是走两步的走法数。
  2. 状态转移方程

    • 如果上一步是走一步(dp[i][0]),那么这一步可以走一步或两步:

      • dp[i+1][0] += dp[i][0] (走一步)
      • dp[i+2][1] += dp[i][0] (走两步)
    • 如果上一步是走两步(dp[i][1]),那么这一步只能走一步:

      • dp[i+1][0] += dp[i][1] (走一步)
  3. 初始条件

    • dp[0][0] = 1 (从第0层开始,上一步是走一步的走法数为1)
    • dp[0][1] = 0 (从第0层开始,上一步是走两步的走法数为0)
  4. 最终结果

    • 到达第 n 层的总走法数为 dp[n][0] + dp[n][1]

实现

def solution(n):
    # 初始化dp数组
    dp = [[0, 0] for _ in range(n + 1)]
    
    # 初始条件
    dp[0][0] = 1
    dp[0][1] = 0
    
    # 状态转移
    for i in range(n):
        dp[i + 1][0] += dp[i][0]  # 从i层走一步到i+1层
        if i + 2 <= n:
            dp[i + 2][1] += dp[i][0]  # 从i层走两步到i+2层
        dp[i + 1][0] += dp[i][1]  # 从i层走一步到i+1层
    
    # 最终结果
    return dp[n][0] + dp[n][1]

if __name__ == "__main__":
    print(solution(2) == 2)
    print(solution(3) == 3)
    print(solution(4) == 4)

实现代码解读

  • 初始化:创建一个二维数组dp,大小为n+1,用于存储每层楼梯的走法数。
  • 状态转移:通过循环遍历每一层楼梯,根据状态转移方程更新dp数组。
  • 边界条件:在循环中,当i + 2超过n时,不再考虑走两步的情况。
  • 输出结果:返回dp[n][0] + dp[n][1],即到达第n层的总走法数。

复杂度分析

  • 时间复杂度:O(n),因为需要遍历每一层楼梯。
  • 空间复杂度:O(n),因为需要存储每一层楼梯的走法数。

算法的优化

虽然当前的解决方案已经能够解决问题,但在某些情况下,可以考虑进一步优化算法:

  1. 空间优化:由于只需要存储上一步和当前步的信息,实际上可以只使用一个一维数组来存储状态,从而将空间复杂度从O(n)降低到O(1)。
  2. 时间优化:在某些情况下,如果楼梯层数n非常大,可以考虑使用更高效的算法或者并行计算来减少计算时间。

动态规划的应用

  动态规划是一种解决问题的思想,它将复杂问题分解为更简单的子问题,并存储这些子问题的解以避免重复计算。在有限制的楼梯攀登问题中,动态规划的应用体现在以下几个方面:

  1. 子问题定义:问题被分解为到达每一层楼梯的走法数,这是一个典型的子问题定义。
  2. 最优子结构:到达第i层的走法数可以由到达第i-1层和第i-2层的走法数推导出来,这体现了问题的最优子结构特性。
  3. 重复子问题:在计算过程中,相同的子问题会被多次计算,动态规划通过存储这些子问题的解来避免重复计算。