每日算法——动态规划之Fibonacci数列

Outline
Fibonacci数列问题
最优子结构和递推表达式
Fib问题的各类变种
青蛙跳-台阶跳
硬币找零问题(敬请期待)

一、Fibonacci数列

1.1 最优子结构和递推表达式

在很多生活场景中,我们都会遇到求解F(n) = F(n-1) + F(n-2)这类问题。它是一类非常重要的经典的简单的动态规划问题。首先F(n)的解是由F(n-1)和F(n-2)组成,而F(n-1)的解是由F(n-1-1),F(n-1-2)组成,因此求解该类问题的过程就是不断寻找F(n-x)的解的过程,因此F(n-x)都F(n)的子问题。具备子问题的问题都可以大化小,小化0与1,大大降低问题复杂度。简单来说,我们把这种具备子问题的特征叫做最优子结构性质。
找到子问题后,求解子问题,显然这个问题中子问题最终会转化为F(1), F(0),规定F(0) = 1;F(1) = 1,因此子问题F(2)= 1+1可以轻松求解。获取到子问题的解是不够的,我们最终的目的是得到原问题的解,因此需要找到子问题和原问题之间的关系,在Fibonacci问题中轻松获取两者关系F(n) = F(n-1) + F(n-2)。我们把两者的关系叫做递推表达式。
有了子问题和递推表达式,那么求解原问题就迎刃而解了。
回到Fibonacci问题,注意到F(n-1) = F(n -2) + F(n-3) ,F(n-2)即用于求解F(n)也用于求解F(n-1)那么我们将第一次求解F(n-2)获取的值保存起来,那么就不必重复计算,加快效率。因此我们将cumbersomeO(2^n)的算法改进为O(n)的算法。

//cumbersome version
long long cumbFib(int n)
{
    if (n == 0)
    {
        return 1;
    }
    if (n == 1)
    {
        return 1;
    }
    return cumbFib(n-1) + cumbFib (n-2);
}
// advanced version
long long modFib(int n,vector<long long> memo)
{
    if (memo[n] != 0)
    {
        return memo[n];
    }
    memo[n] = modFib(n-1,memo) + modFib(n-2,memo);
    return memo[n];
}
// another advance version -非递归方法
long long modFib2(int n, vector<long long> memo)
{
    memo[1] = 1;
    memo[0] = 1;
    for(int i = 2 ; i <= n; i++)
    {
        memo[i] = memo [i-1] + memo [i-2];
    }
    return memo[n];
}

利用数组将每次计算得到的数据先保存起来,用空间换时间。然鹅实际上,我们还可以继续优化。由于没次计算只需要用到两个旧数据,那么我们直接用变量保存这两个旧数据就ok啦。

long long smartFib(int n){
    long long pre = 1,ppre = 1,cur ;
    for (int i = 2;i <= n; i ++)
    {
        cur = pre + ppre;
        pre = ppre;
        ppre = cur;
    }
    return cur;
}

好啦,写到这里,我想应该不能再继续优化了吧!(嗯哼,any question?)那么接下来讨论此类问题的具体应用场景。

1.2 具体应用

1.2.1 青蛙跳问题

在这里插入图片描述

这个题目是经典的Fibonacci数列问题,甚至没有一点改动。值得注意的一点是Note:0 <= n <= 100,如果没有取模,那就必须考虑大数越界问题了。测试发现,当n >=92 时,C++ 中8字节long int已经无法表示其结果啦。n>=95时,最大范围的Unsigned long long也扑街了,C++数据类型范围表如下表所示(参考C官网文件)。至于Python则不需要考虑,因为python中整形数字的大小取决计算机的内存 ,暂且无需考虑大数越界问题。
在这里插入图片描述
在这里插入图片描述

Reference
https://en.cppreference.com/w/c/types/limits

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值