动态规划入门必看-----斐波那契数列/爬楼梯

一 问题引入

1.1 斐波那契数列

斐波那契数列(Fibonacci sequence),又称黄金分割数列,因数学家莱昂纳多·斐波那契(Leonardo Fibonacci)以兔子繁殖为例子而引入,故又称“兔子数列”,其数值为:1、1、2、3、5、8、13、21、34……在数学上,这一数列以如下递推的方法定义:F(0)=0,F(1)=1, F(n)=F(n - 1)+F(n - 2)(n ≥ 2,n ∈ N*)。

我们的目的是求第n项。

1.2 爬楼梯

假设有n个台阶,此时我们从最底下,要爬到第n个台阶,每次可以选择上一个台阶或者两个台阶,问:总共用多少种不同的方法到达第n个台阶。

问题分析:

可以将问题转化为如下格式,从初始方格走到终点方格,其中每次可走一格或者两格。

e5119c027fd04c138683c181660efba2.png

假设用dp[i]表示到第i个台阶总共有多少种,那么显然dp[0] = 0 or 1(看题目和个人理解,可以是0 or 1)

要上到第一个台阶,只能向前走一步(两步就已经超过了),那么dp[1] = 1

要走上第二个台阶,因为每次只能走一个台阶或者两个台阶,所以只能由初始台阶走两格到达,或者第一个台阶走一步到达,即有图中路线1和路线2。那个到达第二个台阶总共的方法种类应该就是由到达初始台阶的种类数加上到达第一个台阶的种类数,即有dp[2] = dp[0] + dp[1]

同样的分析,要到第三个台阶,只能由第一个台阶上两步或者第二个台阶上一步 ,即有dp[3] = dp[1] + dp[2]

第四个台阶 ............ dp[4] = dp[2] + dp[3]

.

.

.

第n个台阶,只能由第n-2个台阶上两步或者第n-1个台阶上一步(路线3和4),即有dp[n] = dp[n-1] + dp[n-2]

最后稍加分析,惊奇的发现居然和斐波那契数列的递推关系不谋而合。

二 代码实现(c++)

1.1 递归未简化版本

int Fibonacci(int n)
{
    if(n == 0)//F(0) = 1
        return 1;
    if(n == 1)//F(1) = 1
        return 1;
    return Fibonacci(n-1) + Fibonacci(n-2);// F(n)=F(n - 1)+F(n - 2)
}

1.2 简化递归版本

此时乍一看没有问题,仔细一分析,存在大量的重复计算,例如以5为例。
F(5)=F(4)+F(3)
F(4)=F(3)+F(2)
F(3)=F(2)+F(1)

计算F(5)时会计算F(4)和F(3),而计算F(4)时,也会计算F(3),F(3)显然被计算了两遍,后续的F(2) F(1) 也被重复计算,因此,我们需要设置一个"记录本",对已经计算过的元素直接返回。

int Fibonacci(int n,vector<int>&dp)//dp的大小可初始为n+1,所有元素初始为-1
{
    //先查记录本
    if(dp[n] != -1)//已经计算过
        return dp[n];
    if(n == 0)//F(0) = 1
        {
            dp[0] = 1;
            return dp[0];
        }
    if(n == 1)//F(1) = 1
        {
            dp[1] = 1;
            return dp[1];
        }
    dp[n] = Fibonacci(n-1) + Fibonacci(n-2);// F(n)=F(n - 1)+F(n - 2)
    return dp[n];
}

1.3 未简化迭代版本

int  Fibonacci(int n)
{
    vector<int>dp(n+1,1);//初始dp[0] dp[1]为1
    
    for(int i = 2;i <= n ;i ++)
        dp[i] = dp[i-2] + dp[i-1];
    
     return dp[n];
}

1.4 简化迭代版本

观察上面迭代版本不难发现,计算dp[i]时只需要前两项即可,没必要存储这么多,只需存储前两项即可。

int Fibonacci(int n)
{
    int f0 = 1;
    int f1 = 1;
    for(int i = 2;i <= n;i ++)
    {
        int curr = f0 + f1;//计算当前项
        f0 = f1;//
        f1 = curr;
    }
    return f1;
}

如果这篇文章对你有帮助,请关注我,有任何疑问,可发评论区,如有错误,欢迎指正,谢谢你的观看。

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值