P17106 题解

July 16, 2026

动态规划DP,记忆化搜索

题目简述

有 $n$ 个台阶。从最下面走到最上面。

一步可以向上走 $1$ 个台阶或 $2$ 个台阶,不能连续两次都向上走 $2$ 个台阶。

请问从 $0$ 走到第 $n$ 级台阶一共有多少种方案。

数据规模与约定

  • 对 $50\%$ 的测试点,$n \leq 5$。
  • 对 $80\%$ 的测试点,$n \leq 20$。
  • 对 $100\%$ 的测试点,$1 \leq n \leq 60$。

30分解法

分析题目,发现该题与 [P1255 数楼梯] 比较相似,属于记忆化搜索的问题。
不难发现一个性质,走到第 $n$ 级楼梯只有两种途径:

  • 从第 $n-1$ 级向上走 $1$ 级
  • 从第 $n-2$ 级向上走 $2$ 级

每一个节点都可由其子节点推导,我们设到第 $n$ 级的方案数为 $ dp_n $ 只需确定两个初始节点 $$dp_1=1, dp_2=2$$ 便可求出所有结果,因此这是一个可以通过递归求解的问题
于是我们列出如下状态转移方程:$$ dp_n = dp_{n-1} + dp_{n-2} $$ 并写出如下代码:

#include<bits/stdc++.h>
#define maxn 62
using namespace std;

signed main(){
    int n;
    cin >> n;
    int dp[maxn];
    dp[1] = 1, dp[2] = 2;
    for(int i = 3; i <= n; ++ i)
        dp[i] = dp[i-2]+2;
    cout << dp[n];
    return 0;
}

提交代码,发现只有 $30$ 分,原因很明显,正如题面所说

不能连续两次都向上走 $2$ 个台阶。

正解

既然不能连续两次走两个台阶,那么上述状态转移的过程必须分类讨论,

$dp_n$ 能由 $dp_{n-2}$ 转移当且仅当 $dp_{n-2}$ 的前一步只走了一步

我决定单独开两个数组,令 $dp1_n$ 和 $dp2_n$,分别记录

到达第 $n$ 级楼梯前最后一步走了 $1$ 步和 $2$ 步的方案数

并且保留之前所设 $dp_n$ 记录总方案数

这三个数组间存在三个相关联的性质:

  • 第 $n-1$ 级向上走一步就到第 $n$ 级,即 $$dp1_n=dp_{n-1}$$
  • 如果到第 $n-2$ 级前最后只走了 $1$ 步,那么可以一次性走 $2$ 级到达第 $n$ 级,即 $$dp2_n=dp1_{n-2}$$
  • 爬到第 $n$ 级的方案总数等于最后一步为 $1$ 步与 $2$ 步方案数的总和,即 $$dp_n=dp1_n+dp2_n$$

化简可得

$$ dp_n = dp_{n-1} + dp_{n-3} $$

可以这么理解此式:

从第 $n-1$ 级向上走一步 和 从第 $n-3$ 级先向上一步再向上两步 这两种方案涵盖了所有情况

综上,只需心算并初始化最底层三级楼梯的数据

dp[1] = 1, dp[2] = 2, dp[3] = 3;

便可递推求出所有答案

最后需要注意的是,题目里 $n$ 的范围较大,需要开 long long 防止整型溢出

正解代码

#include<bits/stdc++.h>
#define int ll 
#define maxn 62

using ll = long long;
using namespace std;

signed main(){
    int n;
    cin >> n;
    int dp[maxn];
    dp[1] = 1, dp[2] = 2, dp[3] = 3;
    for(int i = 4; i <= n; ++ i)
        dp[i] = dp[i-1] + dp[i-3];
    cout << dp[n];
    return 0;
}

返回 信竞专栏