经典爬楼梯
题面如下:
数楼梯
题目描述
楼梯有 N N N 阶,上楼可以一步上一阶,也可以一步上二阶。
编一个程序,计算共有多少种不同的走法。
输入格式
一个数字,楼梯数。
输出格式
输出走的方式总数。
样例 #1
样例输入 #1
4
样例输出 #1
5
提示
- 对于 60 % 60\% 60% 的数据, N ≤ 50 N \leq 50 N≤50;
- 对于 100 % 100\% 100% 的数据, 1 ≤ N ≤ 5000 1 \le N \leq 5000 1≤N≤5000。
最近刷到递归专题,然后看到这个爬楼梯,当时想不就是经典的递归的应用嘛
于是马上写了一个简单的递归程序,体会这个代码就能对递归有进一步的理解
int ans;
void dfs(int u) {
if (u == n)
ans++;
else if (u < n) {
dfs(u + 1);
dfs(u + 2);
}
}
思路是这样子的:
深度优先搜索,
先找一种方法,看看能不能满足(刚好到第n层阶梯) 满足ans++
再找另一种方法
…到头为止
递归结束条件: 到头了
(u>=n)
上面代码的写法 else if()即没到头 那么此外的就是到头有关
然后特判了如果刚好到n层 即if(u==n) ans++
然后dfs 参数为void 函数执行完毕 无需手动return (当然也可以自行末尾加return)
当时我以为这样就结束了? 但是oj狠狠打脸
走楼梯"简单版"
原因是我超时了,为什么会超时,想了一会,超时应该是和栈有关,应该是爆栈了,
通俗的讲就是调用了太多的dfs()函数
既然爆栈了,那么我的第一想法就是掉头跑路,实施planB
之前没有系统刷过递推
但是这题我第一想到的就是递归转递推(我也不知道我什么思路)
好吧不打幌子
这里插入一个背景介绍(bushi):
上学期学计算机竞赛入门,老师讲递推(有时候提前给结论会比一个细节一个细节重复下去容易体会/换一个思路 上学期讲这个爬楼梯递推实现让我感觉摸不着头脑/也有可能是过了这么久学的东西变多了容易体会)
好吧,我已经忘记当时是怎么讲的了
但是这里这个思路可以先体会一下(我也不知道用的对不对)
我把它当作动态规划了 (既然是递推嘛,多多少少跟dp扯点关系)
废话不多说:
集合表示: f[i] :到第i层的方法数
属性: 数量
状态计算:
①最后一下走一步到第i层: f[i-1]
②最后一下走两步到第i层: f[i-2]
f[i]=f[i-1]+f[i-2];
初始化:
f[0]=1
(地面 只有一种方法 因为本来就在地面初始化成1 对后面从地面直接走两步到第2层有用!)
f[1]=1
(地面走一步到第一层)
然后写着写着发现 这不就是斐波那契数列…这么简单?!?
int n;
cin>>n;
f[0]=f[1]=1;
for(int i=2;i<=n;i++)
f[i]=f[i-1]+f[i-2];
然而
打脸++
难道是爆int了???
改成了 long long
还是爆了
打脸++
这时候挺无语的 因为我在刷递归,(好吧这题单名字叫做递推与递归)
只得了60分
(没看数据范围n上5000了 鬼知道到5000层阶梯有多少种方法)
直接看测试数据(bushi)(已经提交了 如果是oi根本没得看 IOI更没得看 当作学习可以看hhh)
n=500
输出:
225591516161936330872512695036072072046011324913758190588638866418474627738686883405015987052796968498626
太长了看不下去 不用想 高精度跑不掉了(python没烦恼,但是没好好学 从来没拿来用过 忘光光了)
只能背模板了…(bushi 根据思路来写 这样子才能应对)
本来都打算二维了 vector套vector 但是这样写不太行的样子 (弄了好久)
最后发现既然只和i-1和i-2有关
那么就滚动数组的思想吧(一开始也没想那么多 形象的写出来如下)
c=a+b;
a=b;
b=c;
自己看看体会 以后再细说(其实看懂这个也差不多理解了)
贴下代码: 高精度我就不讲了
具体参照高精度模板
#include <iostream>
#include <vector>
using namespace std;
int n, ans;
const int N = 50010;
typedef long long LL;
vector<int> add(vector<int> &A, vector<int> &B) {
vector<int> res;
int t = 0;
for (int i = 0; i < A.size() || i < B.size() || t; i++) {
if (i < A.size())
t += A[i];
if (i < B.size())
t += B[i];
res.push_back(t % 10);
t /= 10;
}
return res;
}
int main(void) {
cin >> n;
vector<int> a, b, res;
a.push_back(1), b.push_back(1);
for (int i = 2; i <= n; i++)
{
res = add(a, b);
a = b, b = res;
}
if (n == 1)
puts("1");//因为是从2开始推的 n==1时侯res没有存东西(你也可以在前面自己改)
else {
for (int i = res.size() - 1; i >= 0; i--)
cout << res[i];
puts("");
}
return 0;
}
Ac了
又水一题… 但是要学会递推转递归/递归转递推 有时候会用上!