ACwing—跳台阶(bfs+dp+递归+记忆化搜索算法)
题目描述
一个楼梯共有 n级台阶,每次可以走一级或者两级,问从第0级台阶走到第n级台阶一共有多少种方案。
输入格式
共一行,包含一个整数 n
输出格式
共一行,包含一个整数,表示方案数。
数据范围
1≤n≤45
样例
输入样例:
5
输出样例:
8
代码一(暴力的BFS):
#include <stdio.h>
int climbStairs(int x) {
if (x == 1) {
return 1;
}
else if (x == 2) {
return 2;
}
else {
return climbStairs(x - 1) + climbStairs(x - 2);
}
}
int main() {
int n;
printf("请输入楼梯的阶数:");
scanf("%d", &n);
int ways = climbStairs(n);
printf("%d 阶楼梯一共有 %d 种跳法。\n", n, ways);
return 0;
}
代码过程分析:
climbStairs(5)
/ \
/ \
climbStairs(4) climbStairs(3)
/ \ / \
/ \ / \
climb(3) climb(2) climb(2) climb(1)
/ \ | | |
/ \ | | |
c(2) c(1) 2 2 1
| |
2 1
climbStairs(5)
= climbStairs(4) + climbStairs(3)
= [climbStairs(3) + climbStairs(2)] + [climbStairs(2) + climbStairs(1)]
= [ [climbStairs(2) + climbStairs(1)] + 2 ] + [2 + 1]
= [ [2 + 1] + 2 ] + 3
= [3 + 2] + 3
= 5 + 3
= 8
步骤分解
-
climbStairs(5)
-
调用
climbStairs(4)
和climbStairs(3)
,等待它们的返回值。
-
-
计算
climbStairs(4)
:-
调用
climbStairs(3)
和climbStairs(2)
,等待返回值。 -
计算
climbStairs(3)
:-
调用
climbStairs(2)
和climbStairs(1)
。-
climbStairs(2)
返回 2(基准条件)。 -
climbStairs(1)
返回 1(基准条件)。
-
-
合并结果:
2 + 1 = 3
。
-
-
计算
climbStairs(2)
:-
直接返回 2(基准条件)。
-
-
合并结果:
3 + 2 = 5
。 -
climbStairs(4)
返回 5。
-
-
计算
climbStairs(3)
:-
调用
climbStairs(2)
和climbStairs(1)
。-
climbStairs(2)
返回 2。 -
climbStairs(1)
返回 1。
-
-
合并结果:
2 + 1 = 3
。 -
climbStairs(3)
返回 3。
-
-
合并最终结果:
-
climbStairs(5) = climbStairs(4) + climbStairs(3) = 5 + 3 = 8
。 -
对结果取模
1e9 + 7
(因8 < 1e9 + 7
,结果仍为 8)。
-
代码二(记忆化搜索):
#include <stdio.h>
const int N=45;
int mem[N];
int climbStairs(int x) {
if(mem[x]) return mem[x];
int sum=0;
if (x == 1) {
sum=1;
}
else if (x == 2) {
sum=2;
}
else {
sum=climbStairs(x - 1) + climbStairs(x - 2);
}
mem[x]=sum;
return sum;
}
int main() {
int n;
printf("请输入楼梯的阶数:");
scanf("%d", &n);
int ways = climbStairs(n);
printf("%d 阶楼梯一共有 %d 种跳法。\n", n, ways);
return 0;
}
代码过程分析:
climbStairs(5) → 8
/ \
/ \
climbStairs(4) →5 [climbStairs(3) →3 (cached)]
/ \ / \
/ \ / \
climb(3)→3 climb(2)→2 climb(2)→2 climb(1)→1 (cached)
/ \
/ \
c(2)→2 c(1)→1 (cached)
记忆化数组 mem 的最终状态
索引1 2 3 4 5
值 1 2 3 5 8
-
步骤分解
-
初始化全局数组
mem
所有元素初始值为0
。 -
调用
climbStairs(5)
-
检查
mem[5]
是否为0
(初始为0
),进入计算分支。 -
递归调用
climbStairs(4) + climbStairs(3)
。
-
-
计算
climbStairs(4)
-
mem[4] = 0
,进入计算分支。 -
递归调用
climbStairs(3) + climbStairs(2)
。-
计算
climbStairs(3)
:-
mem[3] = 0
,进入计算分支。 -
递归调用
climbStairs(2) + climbStairs(1)
。-
计算
climbStairs(2)
:-
mem[2] = 0
,赋值sum = 2
,存入mem[2] = 2
,返回2
。
-
-
计算
climbStairs(1)
:-
mem[1] = 0
,赋值sum = 1
,存入mem[1] = 1
,返回1
。
-
-
-
sum = 2 + 1 = 3
,存入mem[3] = 3
,返回3
。
-
-
计算
climbStairs(2)
:-
mem[2] = 2
(已缓存),直接返回2
。
-
-
-
sum = 3 + 2 = 5
,存入mem[4] = 5
,返回5
。
-
-
计算
climbStairs(3)
(在climbStairs(5)
中)-
mem[3] = 3
(已缓存),直接返回3
。
-
-
合并结果
-
climbStairs(5) = 5 (climbStairs(4)) + 3 (climbStairs(3)) = 8
。 -
存入
mem[5] = 8
,最终返回8
。
-
-
记忆化存储:每个
climbStairs(x)
的结果存入mem[x]
,避免重复计算。 -
递归逻辑:
-
climbStairs(x) = climbStairs(x-1) + climbStairs(x-2)
(斐波那契数列)。 -
基准条件:
x=1
返回1
,x=2
返回2
。
-
代码三(递归(dp)):
#include <stdio.h>
const int N=45;
int f[N];
int n;
int main() {
scanf("%d",&n);
f[1]=1,f[2]=2;
if(n==1||n==2){
printf("%d",f[n]);
return 0;
}
for(int i=3;i<=n;i++){
f[i]=f[i-1]+f[i-2];
}
printf("%d",f[n]);
return 0;
}