一、递归
已知斐波那契数列 Fn=Fn−1+Fn−2(n>=3),F1=1,F2=1 用递归的方法求解该数列的第n项。
输入格式:
输入一个正整数n (1<=n<=40)。
输出格式:
输出一个数,数列的第n项
输入样例1:
1
结尾无空行
输出样例1:
1
结尾无空行
输入样例2:
3
结尾无空行
输出样例2:
2
结尾无空行
答案:
#include <bits/stdc++.h>
using namespace std;
int myfunction(int n)
{
if (n <= 2)
return 1;
else
return myfunction(n - 1) + myfunction(n - 2);
}
int main()
{
int n;
cin >> n;
cout << myfunction(n);
return 0;
}
因为所输入的正整数的范围是1~40,所以利用递归而占用的时间与空间都在所承受范围之内。
二、循环
已知斐波那契数列 Fn=Fn−1+Fn−2(n>=3),F1=1,F2=1
求解该数列的第n项,结果对998244353取模。
输入格式:
输入一个正整数n (1<=n<=10000000)。
输出格式:
输出一个数,数列的第n项
输入样例1:
1
结尾无空行
输出样例1:
1
结尾无空行
输入样例2:
3
结尾无空行
输出样例2:
2
结尾无空行
答案:
#include <bits/stdc++.h>
using namespace std;
int myfunction(int n)
{
if (n <= 2)
return 1;
else
{
int x = 1, y = 1, z, i;
for (i = 3; i <= n; i++)
{
z = (x + y) % 998244353;
x = y;
y = z;
}
return z;
}
}
int main()
{
int n;
cin >> n;
cout << myfunction(n);
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项
输入样例1:
1
结尾无空行
输出样例1:
1
结尾无空行
输入样例2:
3
结尾无空行
输出样例2:
2
结尾无空行
答案:
#include <bits/stdc++.h>
using namespace std;
#define ull unsigned long long
#define m 998244353
typedef struct matrix
{
ull node[2][2];
} ma;
ma chengfa(ma a, ma b)
{
ma c;
int i, j, k;
for (i = 0; i < 2; i++)
for (j = 0; j < 2; j++)
{
c.node[i][j] = 0;
for (k = 0; k < 2; k++)
{
c.node[i][j] += a.node[i][k] * b.node[k][j];
c.node[i][j] %= m;
}
}
return c;
}
ma jzksm(ull n, ma x)
{
ma y;
y.node[0][0] = 1;
y.node[0][1] = 0;
y.node[1][0] = 0;
y.node[1][1] = 1;
while (n)
{
if (n & 1)
y = chengfa(y, x);
x = chengfa(x, x);
n >>= 1;
}
return y;
}
int main()
{
ull n;
cin >> n;
if (n < 3)
cout << 1;
else
{
ma x;
x.node[0][0] = 1;
x.node[0][1] = 1;
x.node[1][0] = 1;
x.node[1][1] = 0;
x = jzksm(n - 2, x);
cout << (x.node[0][0] + x.node[0][1]) % m;
}
return 0;
}
当问题规模再次变大之后,如果依然使用简单的for循环来解决问题,那么所消费的时间会是很长的。
总结:
快速幂与慢速幂
1、快速幂
快速幂用来求一个大数的大数次方,如果指数太大,for遍历时间复杂度为O(n),快速幂为O(logn)。
#define ull unsigned long long
ull ksm(ull x, ull n)
{
ull ans = 1;
while (n)
{
if (n & 1)
ans *= x;
x *= x;
n >>= 1;
}
return ans;
}
2、慢速幂
慢速幂用来求两个大数的乘法。和快速幂比较相似,只不过是把乘法换成了加法。
#define ull unsigned long long
ull msm(ull x, ull n)
{
ull ans = 0;
while (n)
{
if (n & 1)
ans += x;
x += x;
n >>= 1;
}
return ans;
}
3、混合用
当快速幂中的底数也是大数的时候,在进行乘法运算时可能会出现超出ull范围的情况,这个时候就需要快速幂和慢速幂相结合。
#define ull unsigned long long
ull msm(ull x, ull n)
{
ull ans = 0;
while (n)
{
if (n & 1)
ans += x;
x += x;
n >>= 1;
}
return ans;
}
ull ksm(ull x, ull n)
{
ull ans = 1;
while (n)
{
if (n & 1)
ans = msm(ans, x);
x = msm(x, x);
n >>= 1;
}
return ans;
}
快速幂与矩阵快速幂
上面说到快速幂是求一个数的n次幂(当n为大数时),那么不难理解,矩阵快速幂就是求矩阵的n次幂(当n为大数时)。需要做出变化的是矩阵的乘法需要我们自己去写函数来实现,矩阵这种特殊的数据类型需要我们自己来定义,其他的只需要照搬快速幂就好了。
#define ull unsigned long long
#define scale 100 //规模自己设定 但是矩阵一定是方阵
typedef struct matrix
{
ull node[scale][scale];
} ma;
ma chengfa(ma a, ma b)
{
ma c;
int i, j, k;
for (i = 0; i < scale; i++)
for (j = 0; j < scale; j++)
{
c.node[i][j] = 0;
for (k = 0; k < scale; k++)
c.node[i][j] += a.node[i][k] * b.node[k][j];
}
return c;
}
ma ksm(ma x, ull n)
{
ma ans;
int i, j;
for (i = 0; i < scale; i++)
for (j = 0; j < scale; j++)
{
if (i == j)
ans.node[i][j] = 1;
else
ans.node[i][j] = 0;
}
//单位矩阵
while (n)
{
if (n & 1)
ans = chengfa(ans, x);
x = chengfa(x, x);
n >>= 1;
}
return ans;
}