赞
踩
问题链接:https://www.acwing.com/problem/content/description/207/
对于斐波那契数来说,每一项都等于他的前两项之和,他的第 0 0 0项和第 1 1 1项分别为 0 , 1 0,1 0,1,那么因为有重复的地方,所有可以使用递归来进行求解
#include <iostream>
using namespace std;
int f_final(int n) {
if(n < 2) return n;
return f_final(n-1) + f_final(n-2);
}
int main() {
int n;
cin >> n;
cout << f_final(n) << endl;
return 0;
}
递归计算的节点个数是
O
(
2
n
)
O(2^n)
O(2n)的级别的,存在大量重复计算。
时间复杂度是
O
(
2
n
)
O(2^n)
O(2n),一秒内大约能算到第四十项左右。
重复计算的原因
每一个递归的程序,都可以绘制一棵树,以表示他的执行过程
由此可以发现,递归的过程中,因为没有记忆话的能力,所有他做了很多的无用功,大量的重复计算,可以使用记忆化进行优化
可以开辟一个数组用于记录计算过的值,下一次再被搜索到的时候,就可以直接得到结果,而不需要继续递归调用了
#include <iostream> using namespace std; const int N = 1e6 + 10, mod = 1e9 + 7; int f[N]; int f_final(int n) { if(f[n]) return f[n]; if(n < 2) return f[n] = n; return f[n] = (f_final(n-1) + f_final(n-2)) % mod; } int main() { int n; cin >> n; cout << f_final(n) << endl; return 0; }
总共有 n n n 个状态,计算每个状态的复杂度是 O ( 1 ) O(1) O(1),所以时间复杂度是 O ( n ) O(n) O(n)。
一秒内可以算 n = 1 0 7 n=10^7 n=107 ,但由于是递归计算,递归层数太多会爆栈,大约只能算到 n = 1 0 5 n=10^5 n=105 级别
#include <iostream> using namespace std; const int N = 1e6 + 10, mod = 1e9 + 7; int f[N]; int f_final(int n) { f[1] = 1; for(int i = 2; i <= n; i++) f[i] = (f[i-1] + f[i-2]) % mod; return f[n]; } int main() { int n; cin >> n; cout << f_final(n) << endl; return 0; }
总共计算
n
n
n 个状态,所以时间复杂度是
O
(
n
)
O(n)
O(n)。
但是他的瓶颈在于空间复杂度,需要开一个长度是
n
n
n 的数组,当
n
=
1
0
8
n=10^8
n=108 时,需要的内存是
4
×
1
0
8
1024
×
1024
≈
381
M
B
\frac{4\times10^8}{1024\times1024}\approx381MB
1024×10244×108≈381MB,很容易超内存
从上面递推的过程中,可以发现,对于每一个状态,他只与前面两个状态有关,所有就可以对空间进行一次优化,我们不需要一直报存前面的每一个状态的结果
#include <iostream> using namespace std; const int mod = 1e9 + 7; int f_final(int n) { if(n < 2) return n; int a = 0, b = 1, c; for(int i = 2; i <= n; i++) { c = (a + b) % mod; a = b; b = c; } return c; } int main() { int n; cin >> n; cout << f_final(n) << endl; return 0; }
时间复杂度还是 O ( n ) O(n) O(n),空间复杂度变成了 O ( 1 ) O(1) O(1)。
本题中,他的数据范围非常的大,上面的方法都会超时的,必须加以优化
对于斐波那契数列中,相邻的两项
f
n
f_n
fn和
f
n
−
1
f_{n-1}
fn−1,可以进行如下变化:
{
f
n
f
n
−
1
}
\left\{
⇒
{
f
n
−
1
+
f
n
−
2
f
n
−
1
}
\Rightarrow \left\{
⇒
{
1
×
f
n
−
1
+
1
×
f
n
−
2
1
×
f
n
−
1
+
0
×
f
n
−
2
}
\Rightarrow \left\{
⇒
{
1
1
1
0
}
×
{
f
n
−
1
f
n
−
2
}
\Rightarrow \left\{
所以说,想要计算第
n
n
n项,可以推导出这样一个公式
{
1
1
1
0
}
n
−
1
×
{
f
1
f
0
}
\left\{
⇒
{
1
1
1
0
}
n
−
1
×
{
1
0
}
\Rightarrow \left\{
#include <iostream> #include <algorithm> #include <cstring> using namespace std; typedef long long LL; const int MOD = 10000; // 二维矩阵连乘 void Mul(int a[][2],int b[][2],int c[][2]) { int t[][2] = { {0,0}, {0,0} }; for(int i = 0; i < 2; i++) for(int j = 0; j < 2; j++) for(int k = 0; k < 2; k++) t[i][j] = (t[i][j] + a[i][k] * b[k][j]) % MOD; memcpy(c,t,sizeof t); } int f_final(int n) { if(n < 2) return n; int res[][2] = { {1,0},{0,1} }; // 1 int t[][2] = { {1,1},{1,0} }; int k = n - 1; while(k > 0) { if(k & 1) Mul(res,t,res); Mul(t,t,t); k >>= 1; } int x[] = {1,0}; int c[] = {0,0}; for(int i = 0; i < 2; i++) for(int j = 0; j < 2; j++) c[i] = (c[i] + x[j] * res[j][i]) % MOD; return c[0]; } int main() { int n; while(cin >> n , n != -1) { cout << f_final(n) << endl; } return 0; }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。