算例一【N 阶楼梯上楼问题(九度教程第93 题)】
-
题目描述
-
解题思路
①由于走到最后一阶n的前一步只有两种可能:1)走到n-1阶->再走一阶 2)走到n-1阶->直接走到n阶,所以走到n阶的可能共有F(n-2)+F(n-1)种
②大致判断答案的范围,90用int可保存不下,所以用longlong吧
-
解题代码
#include <stdio.h>
long long F[91]; //F数组保存数列的每一个值,由于数值过大,我们需要使用long long类型
int main () {
F[1] = 1;
F[2] = 2; //数列初始值
for (int i = 3; i <= 90; i ++)
F[i] = F[i - 1] + F[i - 2]; //递推求得数列的每一个数字
int n;
while (scanf ("%d" ,& n) != EOF ) { //输入
printf ( "%lld\n" ,F[n]); //输出相应的数列数字
}
return 0;
}
-
注意点
①无
算例二【不容易系列之一(九度教程第94 题)】
-
题目描述
-
解题思路
要把所有的信件全都犯错,在数学上,这是一个排列组合的问题(错排)。我们采用如下的步骤进行分析:
符号定义:F(n) F(n)F(n)表示n nn封信全都放错的情况总数。有n封信 1,2,...,k,...,n 其对应的信封为L1,L2,...,Lk,...,Ln现在考虑将第n封信加入到信件的集合中,存在以下两种情况:
①将第n封信放在Lk L_kL k中,且将第k封信放在了Ln 中那么此时只需要对剩余的n−2 封信及其他们刚好对应的n−2个信封进行错排即可对应F(n−2) ,又因为第k kk封是在n−1 封信中挑选出来的有n−1种可能性,所以①对应的情况总数为(n−1)∗F(n−2)
②将第n封信放在Lk 中,但第k封信没有放在Ln 中。也就是说,第k封信不能放在Ln 当中,那么其实在剩余的n-1封信中其实可以把Ln 等价为Lk ,因为对于Ln 的约束只有第k封信不能放进去,那么对于剩余的n-1封信其实就可以将问题等价转化为有n-1封信:1,2,...k,...,n−1 ,和n-1个信封:L1,L2,...,Lk,...Ln−1 。那么对这n-1封信进行错排就会有F(n-1)种情况,和之前一样第k 封是在n−1 封信中挑选出来的有n−1 种可能性,所以②对应的情况总数为(n−1)∗F(n−1)
所以综上所述:可以等到如下递推方程:F(n)=(n−1)∗F(n−1)+(n−1)∗F(n−2)
以上就是数学上常说的错排公式。
那么有了如上的递推方程,我们就可以采用递归/迭代的方式进行求解。这里推荐采用迭代的方式,因为如果采用递归的话,子问题F(n-1)和F(n-2)的求解会存在大量重复的子问题,导致大量重复的计算,当递归的层数变深之后效率就会变得十分低下。
还有一个需要注意的细节就是该题中N的取值范围为{N|1≤N≤20,N∈Z},而F(20)不在int类型的范围内,需要采用long long类型才会导致结果不会溢出。
【以上原文:https://blog.csdn.net/qq_37053885/article/details/87272083 】
-
解题代码
#include <stdio.h>
long long F[21]; //数值较大选用long long
int main () {
F[1] = 0;
F[2] = 1; //初始值
for (int i = 3; i <= 20; i ++)
F[i] = ( i - 1) * F[i - 1] + ( i - 1) * F[ i - 2]; //递推求得数列每一个数字
int n;
while (scanf ("%d" ,& n) != EOF ) {
printf ( "%lld\n" ,F[n]); //输出
}
return 0;
}
-
注意点
①这一类题目代码往往比较简单,重点是递推过程,难啊!