开门见山,直入主题
一、(1)问题描述:已知斐波那契数列 Fn=Fn−1+Fn−2(n>=3), F1=1,F2=1。求解该数列的第n项。
输入格式:输入一个正整数n(1<=n<=40)。
输出格式:输出一个数,表示数列的第n项。
(2)问题分析:首先我们观察到该问题的问题规模,也就是n的数据范围在1到40之间,综合复杂度分析,采用递归的思想解题最好,很容易理解,这也是我们最容易想到的方法。
(3)代码实现
#include<stdio.h>int main(){
int f(int n);
int n,res;
scanf("%d",&n);
res=f(n);
printf("%d",res);
}
int f(int n){
if(n==1||n==2) return(1);
else return(f(n-1)+f(n-2));
}
二、问题描述:
已知斐波那契数列 Fn=Fn−1+Fn−2(n>=3),F1=1,F2=1
求解该数列的第n项,结果对998244353取模。
输入格式:输入一个正整数n(1<=n<=10000000)。
输出格式:输出一个数,表示数列的第n项。
问题分析:这个问题的问题规模显然比第一种情况大了很多倍,当我们继续用递归算法解决此问题时,你会发现严重超时,甚至出现无法输出结果的情况,这是因为递归调用的过程中系统会为每一层的返回值等存储到一个栈空间,当递归次数超限时,容易堆栈溢出,所以递归算法的运行效率较低。而此时应该考虑到用空间换时间,利用题目中的递推公式解题。
代码实现:
#include<stdio.h>int main(){
long long int n,i;
scanf("%lld",&n);
long long int x1,x2,x3;//注意数据范围
x1=1; x2=1;
if(n<=2){
printf("1");
return 0;
}
for(i=3;i<=n;i++){
x3=(x1+x2)%998244353;
x1=x2;//利用临时变量保存前两项的值
x2=x3;
}
printf("%lld",x3);
return 0;
}
三、问题描述:已知斐波那契数列 Fn=Fn−1+Fn−2(n>=3),F1=1,F2=1
求解该数列的第n项,结果对998244353取模。
提示:矩阵快速幂,unsigned long long的最大值:1844674407370955161(1.8e18)
输入格式:输入一个正整数n(1<=n<=1e18)。
输出格式:输出一个数,表示数列的第n项。
问题分析:所谓矩阵快速幂,类比于数学中的幂函数,矩阵快速幂就是矩阵的多少次方,转化为矩阵的乘法。
所以说求数列的第n项就是
1 1
1 0矩阵的(n-1)次方第一行第一列项,也是n次方的第一行第二列项。
代码实现:
#include<stdio.h>
typedef struct matrix{
unsigned long long a[2][2];
}matrix;//定义2x2的矩阵
int main(){
matrix pow(matrix m1, unsigned long long n);
matrix mul(matrix x,matrix y);
unsigned long long n;
scanf("%lld",&n);
matrix m,result;
m.a[0][0]=1; m.a[0][1]=1;
m.a[1][0]=1;m.a[1][1]=0;
result=pow(m,n);
printf("%lld",result.a[0][1]%998244353);
//第n项就是矩阵
//1 1
//1 0的n次方的第一行第二列项
}
matrix pow(matrix m1, unsigned long long n){ //矩阵的n次幂
matrix mul(matrix x,matrix y);
matrix result;
result.a[0][0]=1;
result.a[0][1]=0;
result.a[1][0]=0;
result.a[1][1]=1;
if(n==1) return m1;
while(n>0){ //相比利用for循环累乘效率更高
if(n%2!=0)result=mul(result,m1);
m1=mul(m1,m1); n=n/2;
}
return result;
//返回矩阵
//1 1
//1 0的n次方的目标矩阵
}
matrix mul(matrix x,matrix y){//两个矩阵相乘,因矩阵行列数都为2,为简便,直接赋值结果
matrix ans;
ans.a[0][0]=(x.a[0][0]*y.a[0][0]+x.a[0][1]*y.a[1][0])%998244353;
ans.a[0][1]=(x.a[0][0]*y.a[0][1]+x.a[0][1]*y.a[1][1])%998244353;
ans.a[1][0]=(x.a[1][0]*y.a[0][0]+x.a[1][1]*y.a[1][0])%998244353;
ans.a[1][1]=(x.a[1][0]*y.a[0][1]+x.a[1][1]*y.a[1][1])%998244353;
return ans;
}
总结:
(1)尽量避免使用递归算法,因为执行效率较低,例如求斐波拉契数列第n项时问题规模偏大情况,每一次递归结果依赖于前两次递归结果(求f(4)需要计算f(3)和f(2)),而且会大量重复计算同一递归结果(求f(4)需要计算f(3)和f(2),求f(3)需要计算f(2)和f(1),这样就重复计算了f(2)).
(2) 递推思想,往往比递归思想解决问题更高效,时间和空间复杂度较低。
(3)如果有兴趣的可以查找 矩阵快速幂的资料学习一下,不难,开拓一下思维,明白数学在编程优化过程中的重要性。