星星之火OIer:斐波那契数列(一)——O(n)算法

不要看我就是来水博客的

很久很久以前(1202年)

列昂纳多·斐波那契提出了这样一个问题::

有个农场主有一对刚出生小兔子

小兔子隔两个月后便会长大

而且会有生殖能力

每对成年兔子每个月可以生下1对小兔子

问n个月后这个农场主有多少对兔子(成年+幼年)

 

可能题目描述有点问题

但是还是可以看出是斐波那契数列对吧

当然有一点基础的程序猿肯定会写一段递归代码::

#include
inline void read(int &x) {
    x=0;
    int f=1;
    char s=getchar();
    while(s<'0'||s>'9') {
        if(s=='-')
            f=-1;
        s=getchar();
    }
    while(s>='0'&&s<='9') {
        x=x*10+s-48;
        s=getchar();
    }
    x*=f;
}
inline void pr(int x) {
    if(x<0) {
        putchar('-');
        x=-x;
    }
    if(x>9)
        pr(x/10);
    putchar(x%10+48);
}//快读快输不解释
int n,m;
int F(int x) {//递归求斐波那契数列
    if(x==1||x==2)//边界条件
        return 1;
    return (F(x-1)+F(x-2))%m;//每一项是前两项的和
}
int main() {
    read(n),read(m);
    pr(F(n));
}

手打的,可能会有点问题

大概是这个意思吧

但我们可以发现

这样做的时间复杂度太高

达到了O(n^2)

前面的小数据还好

越到后面越慢

看看::

到50的时候就很难算出来了

因为他每上一层

都要把前面的数重算一遍

到后面的时间复杂度是指数级的

然后我们发现

其实很多数是重复算了的

比如

f(8)=f(7)+f(6),f(7)=f(6)+f(5)

这里的f(6)就重复算了一次

那我们怎么解决这个问题呢

不难想到,我们可以用一个数组把每一个值对应的f存下来

这就是记忆化搜索::

#include
inline void read(int &x) {
    x=0;
    int f=1;
    char s=getchar();
    while(s<'0'||s>'9') {
        if(s=='-')
            f=-1;
        s=getchar();
    }
    while(s>='0'&&s<='9') {
        x=x*10+s-48;
        s=getchar();
    }
    x*=f;
}
inline void pr(int x) {
    if(x<0) {
        putchar('-');
        x=-x;
    }
    if(x>9)
        pr(x/10);
    putchar(x%10+48);
}//快读快输不解释
int n,m;
int f[1000005];//储存f的值
int F(int x) {
    if(f[x]!=0)//x对应的值已经被计算过
        return f[x];//直接返回
    else if(x==1||x==2)//还是边界条件
        return f[1]=f[2]=1;//还是要记录一下
    else
        return f[x]=(F(x-1)+F(x-2))%m;//当前值已经被计算过
}
int main() {
    read(n),read(m);
    pr(F(n));
}

再来对比一下时间::

10倍差距

除去输入时间还是很快的。。。

当然,我们也可以用递推来做(所以上面基本都是废话)

O(n)递推算法::

#include
inline void read(int &x) {
    x=0;
    int f=1;
    char s=getchar();
    while(s<'0'||s>'9') {
        if(s=='-')
            f=-1;
        s=getchar();
    }
    while(s>='0'&&s<='9') {
        x=x*10+s-48;
        s=getchar();
    }
    x*=f;
}
inline void pr(int x) {
    if(x<0) {
        putchar('-');
        x=-x;
    }
    if(x>9)
        pr(x/10);
    putchar(x%10+48);
}//快读快输不解释
int n,m,f[1000005];
int main() {
    read(n),read(m);
    f[1]=f[2]=1;//初始化
    for(int i=3;i<=n;i++)//从3开始递推,推到n
        f[i]=(f[i-1]+f[i-2])%m;//每一项等于前两项的和
    pr(f[n]);
}

当然,这是对于小数据的简单的斐波那契题目

如果再大点,达到1e9呢

这时,我们就要用更高深的算法::

矩阵加速

详解请看下一讲

你可能感兴趣的:(辅助)