2020.10.13开始打卡每日一题
题目:斐波那锲数列
题目一:给定斐波那锲的前两项,求第n项斐波那锲数列的值,因为数字很大,我们对结果mod(1e9+7)
题目二:给定整数N,代表台阶数,一次可以跨2个或者1个台阶,求多少种走法
题目三:假设农场中成熟的母牛每年会生一头小母牛,并且永远不会死(就是这样)。第一年,农场只有一只成熟的母牛,从第二年开始,母牛开始生小母牛。每只小母牛三年之后成熟,又可以生小母牛。给定正数N,求N年后牛的数量。
题目四:给定一个2N的网格,现在有若干大小为12的板砖(只能竖着或者横着放,必),需要无缝紧密放到网格之中,返回多少种放法。
…
其实这些都是斐波那锲数列,非常有意思啊,哈哈哈哈哈哈。
计算的方法
递归
const int MOD = 1000000007;
int fabonacci(int n)
{
if (n <= 1) return 1;
return (f(n - 1) + f(n - 2)) % MOD;
}
显然,计算的节点个数是 O(2^n) 的级别的,存在大量重复计算。
时间复杂度是 O(2n)O(2n),一秒内大约能算到第三四十项。
剪枝
记录已经计算过的状态,每一次需要计算时查表,时间复杂度O(n)
const int N = 100000, MOD = 1000000007;
int a[N];//计算这个数有没有计算过
int fabonacci2(int n)
{
if (a[n]) return a[n];
if (n <= 1) return 1;
a[n] = f2(n - 1) + f2(n - 2);
a[n] %= MOD;
return a[n];
}
递推
循环递推,时间复杂度还是O(n),但是需要开一个长度是n的数组,需要的内存是:
4 * n / (1024 * 1024)
const int N = 100000000, MOD = 1000000007;
int fabonacci3(int n)
{
a[0] = a[1] = 1;
for (int i = 2; i <= n; i ++ )
{
a[i] = a[i - 1] + a[i - 2];
a[i] %= MOD;
}
return a[n];
}
优化递推
其实我们只需要用前两项来计算后一项,那么如果我们只需要得到第n、项,不需要开一个大的数组。
时间复杂度不变,空间复杂度为O(1)
const int MOD = 1000000007;
int fabonacci4(int n)
{
int x, y, z;
x = y = 1;
for (int i = 2; i <= n; i ++ )
{
z = (x + y) % MOD;
x = y;
y = z;
}
return z;
}
矩阵快速幂
我们知道快速幂可以用超快的速度计算出超过10^8级别的计算。通过找到项与项之间的关系,用矩阵的乘法去计算矩阵快速幂,次方可以用快速幂来优化。
矩阵快速幂:一个函数用来计算矩阵的乘法,另一个是计算矩阵的n次方。
const int N;
int temp[N][N];
//快速幂的模板
void multiplies(int a[][N], int b[][N], int c[][N])
{
memset(temp,0,sizeof(temp));
for (int i = 0; i < N; i ++ )
for (int j = 0; j < N; j ++ )
for (int k = 0; k < N; k ++ )
{
long long x = temp[i][j] + (long long)a[i][k] * b[k][j];
temp[i][j] = x % MOD;
}
for (int i = 0; i < N; i ++ )
for (int j = 0; j < N; j ++ )
c[i][j] = temp[i][j];
}
int res[N][N];
int fastPower(int a[][N],long long n)
{
//int x[2] = {1, 1};
//int res[][2] = {{1, 0}, {0, 1}};
memset(res,0,sizeof(res));
for (int i = 0; i < n ; i++)
{
res[i][i]=1;
//单位矩阵,res*任意一个矩阵都等于本身
}
//int t[][2] = {{1, 1}, {1, 0}};//转移矩阵
//long long k = n - 1;
while (n)
{
if (k&1) multiplies(res, a, res);
multiplies(a, a, a);
n >>= 1;
}
快速幂的实现
这道题呢,需要计算出转移矩阵,然后计算n-1次方,
X1=【x1,x0】,那么Xn=【xn,xn-1】;
A=【1 1 1 0】;
Xn=Xn-1 * A;
递推得到:Xn=X1 * A^(n-1)
先计算A^n,然后再左乘一个X1,就能得到Xn,再取Xn的第一个元素就是答案
const int N;
const int MOD = 1000000007;
void multiplies(int a[][2], int b[][2], int c[][2])
{
int temp[][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 ++ )
{
long long x = temp[i][j] + (long long)a[i][k] * b[k][j];
temp[i][j] = x % MOD;
}
for (int i = 0; i < 2; i ++ )
for (int j = 0; j < 2; j ++ )
c[i][j] = temp[i][j];
}
int res[N][N];
int fastPower(long long n)
{
int x[2] = {1, 1};
//int res[][2] = {{1, 0}, {0, 1}};
memset(res,0,sizeof(res));
for (int i = 0; i < n ; i++)
{
res[i][i]=1;
//单位矩阵,res*任意一个矩阵都等于本身
}
int t[][2] = {{1, 1}, {1, 0}};//转移矩阵
long long k = n - 1;
while (k)
{
if (k&1) multiplies(res, t, res);
multiplies(t, t, t);
k >>= 1;
}
int cur[2] = {0, 0};
for (int i = 0; i < 2; i ++ )
for (int j = 0; j < 2; j ++ )
{
long long r = c[i] + (long long)x[j] * res[j][i];
cur[i] = r % MOD;
}
return cur[0];
}
这其实模板题啊,懂原理学会运用就行。
近期预告:
线段树、树状数组、状压DP、Trie树、图论
如果大家有什么建议或者要求请后台留言
联系方式:shirandexiaowo@foxmail.com
本文介绍了斐波那契数列及其在不同问题中的应用,包括台阶问题、牛的数量增长和放置砖块的问题。讨论了四种计算斐波那契数列的方法:递归、记忆化搜索、循环递推和矩阵快速幂,强调了优化计算效率和空间复杂度的重要性。文章最后预告了即将探讨的技术主题,如线段树和图论。
798

被折叠的 条评论
为什么被折叠?



