题目:
一个小孩上楼梯,楼梯有n阶,小孩每次可以上一阶,两阶或三阶,问一共有几种走法(约定n为0是方法数为1)。
输入在一行给出n。答案对1e9^7取模。
输出在一行给出方法数。
思路:
这是一道很经典的题目,此前也遇到过很多次,但每次回头看都会有一些不一样的想法。要解决这道题,首先肯定是从阶数较小的开始分析。
如果只有一阶楼梯,方法显然只有1种。
如果有两阶楼梯,有2种方法。一种是一次走两阶。一种是先走一阶,再走一阶。
如果有三阶楼梯,则有4种方法。一种是一次走三阶。一种是先走一阶,此时剩下两阶,而两阶的方法数已经算出是两种。还有一种是先走两阶,再走一阶。
如果有四阶楼梯,则有7种方法。一种是先走一阶剩下三阶,三阶的方法数为4种。一种是先走两阶剩两阶,两阶的方法数是2种。一种是先走三阶剩一阶,方法数为1种。所有相加就是7种。
如果有五阶楼梯也是如此,分析先走一阶剩四阶的方法数,先走两阶剩三阶的方法数,先走三阶剩两阶的方法数,最后相加即可。
简单归纳就可得出,求上n阶楼梯的方法数就是将上n-1,n-2,n-3阶楼梯的方法数相加,写成公式就是:
f(n) = f(n-1) + f(n-2) + f(n-3)
递归代码如下,时间复杂度为O(n^3):
#include <bits/stdc++.h>
using namespace std;
const int mod = 1e9+7;
int recursion1(int n);
int recursion2(int n);
int main() {
int n;
cin >> n;
cout << recursion2(n) << endl;
return 0;
}
int recursion1(int n) {
if(n<0) return 0;
else if(n==0 || n==1) return 1;
else if(n==2) return 2;
return recursion(n-1)%mod + recursion(n-2)%mod + recursion(n-3)%mod;
}
同样的思路,用迭代的方式代码如下,时间复杂度为O(n):
#include <bits/stdc++.h>
using namespace std;
const int mod = 1e9+7;
int recursion2(int n);
int main() {
int n;
cin >> n;
cout << recursion2(n) << endl;
return 0;
}
int recursion2(int n) {
if(n<0) return 0;
else if(n==0 || n==1) return 1;
else if(n==2) return 2;
int x1 = 1;
int x2 = 2;
int x3 = 4;
for(int i=4;i<=n;i++) {
int t = x1;
x1 = x2 % mod;
x2 = x3 % mod;
x3 = (x1+x2)%mod + t%mod;
}
return x3;
}
分析从思考到产生代码的过程:
思考时是从小规模开始,我们能解决的问题顺序思考的,这与迭代的代码完全吻合,而递归则是多了一步归纳,将思路总结成递归式表达出来,单看式子,是一个逆序的过程,仿佛是从大规模开始求,(如果一开始思考是从大规模开始分析,这种思想是分治思想),显然从小规模开始更符合人脑的思考方式,而产生的两种代码,只是同一种思路不同的表现方式。
递归的代码在求解问题时并非先求解大规模问题,也是通过层层递归,先求解小规模问题,再层层返回,得出结果,而每一次递归,都会在栈中为其开辟空间,储存变量。所以从性能上分析,就本题而言,迭代是更好的解法,递归好在表达更加简洁。