动态规划 - 斐波那契数列模型

动态规划解决斐波那契数列模型问题

系列文章目录

leetcode - 双指针问题_leetcode双指针题目-优快云博客

leetcode - 滑动窗口问题集_leetcode 滑动窗口-优快云博客

高效掌握二分查找:从基础到进阶-优快云博客

leetcode - 前缀和_前缀和的题目-优快云博客


目录

系列文章目录

前言

1、题1 第 N 个泰波那契数 :

解法一:动态规划

参考代码:

解法二:空间优化版本

参考代码:

2、题2 三步问题:

解法: 动态规划

参考代码:

3、题3 使用最小花费爬楼梯:

解法一:动态规划(以某一个位置为结尾)

参考代码:

解法二:动态规划(以某一个位置为开头)

参考代码:

4、题4 解码方法:

解法:动态规划

参考代码:

优化参考代码:

总结

leetcode - 前缀和_前缀和的题目-优快云博客


前言

路漫漫其修远兮,吾将上下而求索;


大家可以先尝试自己做一下喔~

斐波那契数列模型 

  1. 1137. 第 N 个泰波那契数 - 力扣(LeetCode)
  2. 面试题 08.01. 三步问题 - 力扣(LeetCode)
  3. 746. 使用最小花费爬楼梯 - 力扣(LeetCode)
  4. 91. 解码方法 - 力扣(LeetCode)

1、题1 第 N 个泰波那契数 :

1137. 第 N 个泰波那契数 - 力扣(LeetCode)

思考:

可以将泰波那契数看作是斐波那契数的加强版;

根据题意可知第n个泰波那契等于其前三个泰波那契数之和

解法一:动态规划

动态规划的做题流程,一般会定义一个dp 表(dp 表通常是一个一维数组或者二维数组);一维数组的情况:先创建一个一维数组,该数组通常被称为dp 表,接下来就是填dp 表,其中的某一个值可能就是最后的结果;

我们可以使用动态规划来解决这道题,五个步骤:

1、确定一个动态表达式

2、根据该动态表达式来推导状态转移方程

3、初始化

4、填表顺序

5、返回值

a、状态表示

Q1: 什么是状态表示?

  • dp 表中某一个位置所代表的含义;eg.dp[0] 存了一个值a,那么值a 便会代表一个特殊的含义,其中此含义就是状态表示;

Q2:状态表示是怎么样来的?

一般有三种方式可以来确定:

  • 1、题目怎么要求,我们就怎么定义状态表示
  • 2、经验 + 题目要求
  • 3、分析题目的过程中发现重复的子问题(再将重复的子问题抽象为状态表达式)

注:在本题中可以直接按照题干的要求去定义一个状态表示;本题的目标:返回第n个泰波那契数

我们可以搞一个dp 表,让dp[0] 表示第0个泰波那契数,dp[1] 表示第1个泰波那契数……dp[i] 表示第i 个泰波那契数……我们只需要返回第n 个泰波那契数,即返回 dp[n];

b、状态转移方程

在本题中,我们需要思考的是:dp[i] 怎么求来? 

推导状态转移方程:1、用之前或者之后的状态推导得到dp[i] 的值 2、根据最近的一步来划分问题

本题十分明显由可得:Tn = Tn-1 + Tn-2 + Tn-3 ;故而本题的状态转移方程为:dp[i] = dp[i-1] + dp[i-2] + dp[i-3];

c、初始化

初始化的含义:保证填dp表(根据状态转移方程来调表)的时候不会发生越界;

Q1:为什么要保证不越界?

  • 倘若用此处的状态转移方程:dp[i] = dp[i-1] + dp[i-2] + dp[i-3]; 直接填表,那么 dp[0] = dp[-1] + dp[-2] + dp[-3] ,而 dp[-1] ,dp[-2] ,dp[-3] 就是越界访问;本题中的dp[0] 、dp[1]、dp[2]使用状态转移方程的时候均会发越界,所以这三个值需要单独进行初始化;而如何初始化取决于题干;

在本题中:

d、填表顺序

Q:为什么要研究填表顺序?

  • 为保证填写当前状态的时候所需要的状态已经计算好了;

在本题中,因为dp[i] = dp[i-1] + dp[i-2] + dp[i-3],即填表的时候需要借助于其前三个数据,所以填表顺序为从左往右

d、返回值

返回结果: 结合题目要求+状态表示

本题干:,所以我们返回 dp[n] 即可;

参考代码:

    int tribonacci(int n) 
    {
        //边界情况处理
        if(n==0) return 0;
        else if(n==1 || n==2) return 1;
        //创建一维dp
        //状态表示:dp[i]表示第i个泰波那契数
        //状态转移方程:dp[i] = dp[i-1] + dp[i-2] + dp[i-3]
        //初始化: dp[0]= 0 , dp[1] =1, dp[2] = 1; 
        //填表顺序: 从左往右
        //返回值:dp[n]
        vector<int> dp(n+1);
        //初始化
        dp[0]= 0 , dp[1] =1, dp[2] = 1;
        //填表
        for(int i = 3;i<=n;i++)
        {
            //状态转移方程
            dp[i] = dp[i-1] + dp[i-2] + dp[i-3];
        }
        //返回结果
        return dp[n];
    }

解法二:空间优化版本

注:此处空间优化的技巧只会在这道题以及接下来的背包问题中可用;

关于动态规划的空间优化,一般都是用流动数组的形式来优化;可以将时间复杂度为O(N^2)的动态规划优化成时间复杂度为O(N),将时间复杂度为O(N)的动态规划优化成时间复杂度为O(1);

可以发现,在求某一状态时,仅需要该状态前三个状态就可以求得到本状态;当我们依次从左往右求dp[i] 的时候,dp数组中太靠前的数据反而用不到,显然用不到的数据可以省去的。仅用该数组中有效的若干个状态便可以解决,像这样的情况,我们都可以用滚动数组来解决;创建三个变量进行”流动“,以维护所求状态的前三个状态;

Q:明明是利用变量在滚动的,为什么叫做”滚动数组“?

”滚动数组“只是一个名字而已,取这个名称只是为了统一叫法;

注:三个变量,在实现的时候还可以定义为 int[3] 的数组

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值