问题理解:
你有一座楼梯,楼梯总共有 n 层。你每次可以选择:
- 走一步,或者
- 走两步。
但是有一个额外的限制条件:不能连续两次走两步。
任务是计算出从楼梯底(第 0 层)走到楼梯顶(第 n 层)有多少种不同的方式。
示例:
对于 n=3,我们可以有以下几种走法:
- 走一步,走一步,走一步(3步)。
- 走一步,走两步(2步)。
- 走两步,走一步(2步)。
因此总共有 3 种走法。
动态规划的思路:
我们需要通过动态规划(Dynamic Programming, DP)来求解这个问题。我们可以构造一个状态转移方程,并根据这个方程进行状态的推导,最后得到最终的结果。
1. 定义状态:
我们定义 dp[i][0] 和 dp[i][1]:
dp[i][0]表示到达第i层时,最后一步是走一步的走法数。dp[i][1]表示到达第i层时,最后一步是走两步的走法数。
2. 状态转移方程:
根据题意,我们需要通过两种方式来推导每一层的走法数:
-
到达第
i层,最后一步是走一步:- 这意味着从第
i-1层走一步过来。无论前一层是怎么走的(即dp[i-1][0]或dp[i-1][1]),我们都可以从这里走一步到达第i层。 - 因此,
dp[i][0] = dp[i-1][0] + dp[i-1][1]。
- 这意味着从第
-
到达第
i层,最后一步是走两步:- 这意味着必须从第
i-2层走两步过来,因为根据题目要求,不能连续走两步。所以,只有前一层是走一步(即dp[i-2][0]),才能在此基础上走两步。 - 因此,
dp[i][1] = dp[i-2][0]。
- 这意味着必须从第
3. 边界条件:
dp[0][0] = 1:表示站在第 0 层时,只能保持在原地,即不走。dp[1][0] = 1:表示站在第 1 层时,只有一种方式:走一步。dp[1][1] = 0:不可能从第 -1 层走两步到达第 1 层,因此dp[1][1]为 0。
4. 最终结果:
我们要求的是到达第 n 层的所有走法数,既包括最后一步走一步的情况,也包括最后一步走两步的情况。因此,最终结果为:dp[n][0] + dp[n][1]
动态规划的代码实现:
public class Main {
public static int solution(int n) {
// 特殊情况,楼梯层数为0或1时直接输出
if (n == 0) {
System.out.println(0);
return 0;
}
if (n == 1) {
System.out.println(1);
return 1;
}
// dp[i][0] 表示到达第i层,且最后一步是走一步
// dp[i][1] 表示到达第i层,且最后一步是走两步
int[][] dp = new int[n + 1][2];
// 初始化边界条件
dp[0][0] = 1; // 站在0层,不走的情况
dp[1][0] = 1; // 站在1层,走一步的情况
// 从第二层开始计算
for (int i = 2; i <= n; i++) {
// 计算到达第i层且最后一步是走一步
dp[i][0] = dp[i - 1][0] + dp[i - 1][1];
// 计算到达第i层且最后一步是走两步
dp[i][1] = dp[i - 2][0]; // 只有从i-2层走一步,才能走两步
}
// 输出到达第n层的所有走法
return dp[n][0] + dp[n][1];
}
public static void main(String[] args) {
// Add your test cases here
System.out.println(solution(2) == 2);
}
}
代码解析:
-
边界处理:
- 处理特殊情况:如果楼梯层数为 0 或 1,直接输出结果。
-
DP 数组的初始化:
- 创建一个二维数组
dp[i][0]和dp[i][1],用于存储到达第i层时,最后一步是走一步和走两步的走法数。 - 初始化第 0 层和第 1 层的边界条件。
- 创建一个二维数组
-
DP 状态转移:
- 从第 2 层开始,根据状态转移方程计算每一层的走法数。
-
最终输出:
- 最终的结果是到达第
n层的走法数,即dp[n][0] + dp[n][1]。
- 最终的结果是到达第
复杂度分析:
- 时间复杂度:每层计算只需要常数时间(即对于每个
i,只涉及到前两层的计算)。因此,时间复杂度是O(n)。 - 空间复杂度:我们使用了一个大小为
n+1的二维数组dp来存储每层的状态,空间复杂度是O(n)。
总结:
通过动态规划的方法,我们将问题分解为多个子问题,通过状态转移来逐层计算每种情况下的走法数,最终得到总的走法数。