算法王冠上的明珠——动态规划之斐波那契数列问题

目录

[1. 什么是动态规划](#1. 什么是动态规划)

[2. 动态规划步骤](#2. 动态规划步骤)

状态表示

状态转移方程

初始化

填表顺序

返回值

[3. 例题讲解及具体代码](#3. 例题讲解及具体代码)

[3.1 LeetCode1137. 第 N 个泰波那契数](#3.1 LeetCode1137. 第 N 个泰波那契数)


这篇文章是我第一篇关于动态规划的,所以我会先从什么是动态规划说起。

1. 什么是动态规划

动态规划是一种通过将复杂问题分解为重叠子问题,并利用子问题的解来高效求解原问题的算法思想。它的核心是避免重复计算,通过存储中间结果(即 "记忆化")来优化时间复杂度。

其实简单来说就是通过前面的状态来定义后面的状态,比如说我们前面关于前缀和的文章其实就可以被归为动态规划的一种,只不过它比较简单,所以我把它放在了基础算法里面。

2. 动态规划步骤

做动态规划类题目的步骤就是下面这几步。

状态表示

状态表示就是我们数组对应的那个位置的值的含义,简单来说就是那个值代表着什么。比如说我们前面说的前缀和,那他的状态表示就是代表着原数组前面这些数的累加。

状态转移方程

状态转移方程就是根据上面的状态表示来得到的一个公式,比如说我们前面说的前缀和,它的状态转移方程就是dp[i]=dp[i-1]+nums[i]。

初始化

初始化的作用简单来说就是为了防止数组越界访问,所以我们在一开始会给dp表的一小部分值提前给好。方便我们后续计算。拿前缀和来说就是第0个位子我们会直接给0。

填表顺序

之所以我们要有填表顺序,是因为我们填当前位置的值会使用到前面的一些值,那么我们要确保前面的这些值都已经计算好了。

返回值

返回值就是返回题目要求的那个值。

接下来我们通过几道例题来了解这几个步骤。

3. 例题讲解及具体代码

3.1 LeetCode1137. 第 N 个泰波那契数

接下来我们来讲解下面这道题,题目意思也很简单,就是当前位置的值是由前面三个位置的值得来的。

所以在这道题里面它的状态表示就是当前位置值等于前面三个位置的值的和。

所以在这道题里面它的状态转移方程就是dp[i]=dp[i-1]+dp[i-2]+dp[i-3]。

它的初始化就是第0个位子设置为0,第一个和第二个位置设置为1。

填表顺序就是从前往后就好。

返回值就是第n个位置的值。

我们看下面这个代码,其实我们做动态规划的题目,只要可以把上面的这五步给写出来,那么代码的实现也就变的很简单了。

cpp 复制代码
class Solution {
public:
    int tribonacci(int n) {
        if(n==0) return 0;
        if(n==1||n==2) return 1;
        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];
    }
};

3.2 面试题 08.01. 三步问题

我们看下面这道题就稍微有一点点难了,要我们算的是小孩走到这n步时的方法。

状态表示:dp[n]表示走到当前位置时的方法数。

状态转移方程:这道题的状态转移方程稍微有点难,我们看下面这个图,0代表的是地板。然后我们到a就1种办法(一步),到b有2种(一步一步,两步),到c的方法有4种(一步一步一步,一步两步,两步一步,三步),接着我们就不用自己算了,因为按照题目的要求,从a,b,c位置可以走到d位置,所以我们这里直接就是d前面三个位置办法的和就好了。我一开始在做的时候是想着是前三个位置的办法数加上多少多少,后来经过尝试发现不是。因为我们可以这样去想如果a位置的值要加上多少多少的话,那么就会把b和c的一部分给计算进去了,会造成重复计算,所以我们不要去关心abc是怎么到d位置的,我们要记住每一个位置的值代表的是到该位置的方法数,而一个位置只有它前面的三个可以到,所以加上前面三个的方法数就好了。

PS:如果还是觉得不太理解的话,那么就画个图,把abc的方法都画出来,这样也可以理解。

初始化:在上面已经写了,到a就1种办法(一步),到b有2种(一步一步,两步),到c的方法有4种(一步一步一步,一步两步,两步一步,三步)。

填表顺序:由题可得是从前向后填表。

返回值:返回值就是n位置的方法数。

我们看,只要我们知道上面的那5个,那么代码就可以直接写出来了。

我们在dp[i-1]+dp[i-2]这里也模上了一个1000000007,是因为在这里也会过大。所以我们要先模一下。

cpp 复制代码
class Solution {
public:
    int waysToStep(int n) {
        if(n==1) return 1;
        if(n==2) return 2;
        if(n==3) return 4;
        vector<int> dp(n+1);
        dp[1]=1;
        dp[2]=2;
        dp[3]=4;
        for(int i=4;i<=n;++i)
            dp[i]=((dp[i-1]+dp[i-2])%1000000007+dp[i-3])%1000000007;
        return dp[n];
    }
};
相关推荐
地平线开发者15 小时前
PTQ 量化数值范围与优化
算法·自动驾驶
sali-tec15 小时前
C# 基于halcon的视觉工作流-章68 深度学习-对象检测
开发语言·算法·计算机视觉·重构·c#
测试人社区-小明16 小时前
智能弹性伸缩算法在测试环境中的实践与验证
人工智能·测试工具·算法·机器学习·金融·机器人·量子计算
罗西的思考16 小时前
【Agent】MemOS 源码笔记---(5)---记忆分类
人工智能·深度学习·算法
qq_4335545419 小时前
C++数位DP
c++·算法·图论
武子康19 小时前
大数据-184 Elasticsearch Doc Values 机制详解:列式存储如何支撑排序/聚合/脚本
大数据·后端·elasticsearch
AshinGau19 小时前
Softmax 与 交叉熵损失
神经网络·算法
似水এ᭄往昔20 小时前
【C++】--AVL树的认识和实现
开发语言·数据结构·c++·算法·stl
栀秋66620 小时前
“无重复字符的最长子串”:从O(n²)哈希优化到滑动窗口封神,再到DP降维打击!
前端·javascript·算法
xhxxx20 小时前
不用 Set,只用两个布尔值:如何用标志位将矩阵置零的空间复杂度压到 O(1)
javascript·算法·面试