题目描述
楼梯有 N 阶,上楼可以一步上一阶,也可以一步上二阶。
编一个程序,计算共有多少种不同的走法。
输入格式
一个数字,楼梯数。
输出格式
输出走的方式总数。
输入输出样例
输入 #1
4
输出 #1
5
说明/提示
- 对于 60% 的数据,N≤50;
- 对于 100% 的数据,1≤N≤5000
思路:经典的斐波那契数列问题(它甚至都没有变形)
于是我们可以光速写出代码:
#include<bits/stdc++.h>
using namespace std;
int n;
int update(int n){
if(n == 1) return 1;
if(n == 2) return 2;
return update(n-1) + update(n-2);
}
int main(){
cin>>n;
long long ans = update(n);
cout<<ans<<endl;
return 0;
}
但是,这样就对了吗?
很明显,我们看到数据集最大是5000,这个数量的递归链已经是非常恐怖了,一定会爆栈,因此我们需要进行优化。
思路:记忆化搜索优化。
记忆化搜索:我们先定义一个标记数组, 将其初始化为-1,每次将答案保存在数组的对应位置中,当数组中不为-1时(表明已经保存过结果了),我们直接返回对应的数组,如果没有则将该次递归的答案保存到数组中,这样,我们就完成了记忆化搜索。
#include<bits/stdc++.h>
using namespace std;
const int N = 5050;
int q[N];
int n;
int update(int n){
if(n == 1) return 1;
if(n == 2) return 2;
if(q[n] != -1) return q[n];
return q[n] = update(n-1) + update(n-2);
}
int main(){
cin>>n;
memset(q,-1,sizeof(q));//将数组初始化为-1
long long ans = update(n);
cout<<ans<<endl;
return 0;
}
再次运行测试,发现这次没有TLE了,而是WA,又因为数最大为5000,一些数字应该很大,超出了存储范围,因此我们需要用高精度。
高精度思想:改用字符串进行存储,用数组模拟加法竖式,最后在将字符串倒序输出
于是,我们就得到了最终代码:
#include<bits/stdc++.h>
using namespace std;
const int N = 5050;
int n;
string q[N];
string add(string a, string b) {
int x1 = a.size(), x2 = b.size();
int n = max(x1, x2);
int la[N]={0}, lb[N]={0}, lc[N] = {0}; // 初始化数组为 0
for (int i = 0; i < x1; i++) {
la[i] = a[x1 - i - 1] - '0';
}
for (int i = 0; i < x2; i++) {
lb[i] = b[x2 - i - 1] - '0';
}
for (int i = 0; i < n; i++) {
lc[i] += la[i] + lb[i]; // 累加
if (lc[i] >= 10) { // 进位操作
lc[i + 1]++;
lc[i] -= 10;
}
}
if (lc[n] != 0) n++;//判断最高位是否需要进位
string res;//存储最后倒序输出的字符串
for (int i = n - 1; i >= 0; i--) {
res += (lc[i] + '0');//倒序输出。累加到最后输出结果的字符串上
}
return res;
}
string update(int n) {
if (n == 1) return "1";
if (n == 2) return "2";
if (!q[n].empty()) return q[n];
q[n] = add(update(n - 1), update(n - 2));
return q[n];
}
int main() {
cin >> n;
for (int i = 0; i < N; i++) {
q[i] = "";
}
string cnt = update(n);
cout << cnt << endl;
return 0;
}