当前位置: 首页 > article >正文

动态规划算法的欢乐密码(一):斐波那契数模型

专栏:算法的魔法世界

个人主页:手握风云

目录

一、动态规划

二、例题讲解

2.1. 第 N 个泰波那契数

2.2. 三步问题

2.3. 使用最小花费爬楼梯

2.4. 解码方法


一、动态规划

        动态规划是一种将复杂问题分解为更小的子问题,并利用子问题的解来构建原问题的解的方法。它主要用于解决具有重叠子问题最优子结构特性的问题,通过存储子问题的解(避免重复计算)来提高效率。

        动态规划的解题思路通常可以分为以下几个步骤:(1)定义状态表示;(2)推导状态转移方程;(3)初始化和边界条件;(4)填表顺序;(5)返回值

二、例题讲解

2.1. 第 N 个泰波那契数

        本题可以看作是斐波那契数的加强版,并且首项是从第0项开始计算的。我们先来创建一个一位数组dp[],这个dp表里面的值就是我们的状态表示。题目要求我们求出第N个泰波那契数,那么dp[i]就表示第i个泰波那契数。dp[i]的表示就是状态转移方程,题目其实给出状态转移方程,dp[i]依赖于前三个状态,dp[i]=dp[i-1]+dp[i-2]+dp[i-3]。接下来就是初始化,保证填表的时候不越界,如果我们使用上面的状态表示方程,dp[0]、dp[1]、dp[2]都会发生越界。而题目也已经将前三个状态给了出来,dp[0]=0、dp[1]=1、dp[2]=1。下面我们填表的时候,为了填写当前状态,所需的状态必须是已经计算出来的,所以填表顺序从左到右。

        完整代码实现:

class Solution {public int tribonacci(int n) {if (n == 0)return 0;if (n == 1 || n == 2)return 1;int[] dp = new int[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),因为额外创建了数组,空间复杂度也为O(n)

        空间优化:一般情况下,动态规划都可以利用滚动数组来解决。在这道题中,从第三个数开始,我们仅需前三个数就可以求出当前状态的结果,其他的空间就会产生浪费。使用滚动数组,我们只需要存储最近的三个泰波那契数。如下图,先初始化a、b、c的值,第N个泰波那契数d=a+b+c,当我们要求下一个泰波那契数时,将d的值更新为c,c的值更新为b,b的值更新为a。因为这种求法不用创建数组,空间复杂度可以优化为O(n)

2.2. 三步问题

        当n=1时,只有1种方式。当n=2时,从0直接到2,1种方式;我们不管怎么从0到1,直接考虑从1到2,1种方式,所以0到2一共是1+1=2种方式。当n=3时,从0直接到3,1种方式;从1开始直接到3,1种方式,前面从0到1,1种方式,1*1=1种;从2开始直接到3,1种方式,从0到2,2种方式,2*1=2种方式,所以0到3一共是1+1+2=4种方式;当n=4时,按照上面的推导过程,从0到4一共是1+2+4=7种方式。所以递推公式为:当n>3时,F(n)=F(n-1)+F(n-2)+F(n-3)。

        我们根据题目要求可以得出状态表示为到达n号台阶时,共有多少种方式。这个小孩一次可以上1阶、2阶、3阶台阶,我们以第n阶台阶最近的一步划分,那么状态转移方程为:dp[n]=dp[n-1]+dp[n-2]+dp[n-3]。当n<4时,这个状态转移方程就没有意义了,初始化操作,dp[1]=1,dp[2]=2,dp[3]=4。根据题目要求,填表顺序为从左到右。

        完整代码实现:

class Solution {public int waysToStep(int n) {// 定义模数,用于防止结果溢出int mod = (int) 1e9 + 7;if (n == 1 || n == 2) return n;if (n == 4) return 4;int[] dp = new int[n + 1];// 初始化前三个台阶的方法数dp[1] = 1;dp[2] = 2;dp[3] = 4;// 从第4个台阶开始,计算每个台阶的方法数for (int i = 4; i <= n; i++) {// 到达第i个台阶的方法数为前三个台阶的方法数之和,并取模防止溢出dp[i] = ((dp[i - 1] + dp[i - 2]) % mod + dp[i - 3]) % mod;}return dp[n];}
}

2.3. 使用最小花费爬楼梯

        题目给出了一个长度为n的数组cost,其中cost[i]表示爬到第i个台阶所需要的花费。目标是计算出从底部爬到顶部所需的最小花费。我们首先要明白楼梯顶部在哪里,这里不是数组的最后一个元素,而是越过数组末尾的下一个位置。

  • 第一种解法

        根据题目分析可以得出,本题的状态表示为到达n阶台阶时,最小花费的数目。接着利用之前的状态来推导dp[n]的值,根据最近的一步或者两步来划分问题:先到达dp[n]的前一个位置dp[n-1],然后支付cost[n-1]走一步,到达dp[n]花费为dp[n-1]+cost[n-1];先到达dp[n-2]的前两个位置dp[n-2],然后支付cost[n-2]走两步,花费为dp[n-2]+cost[n-2]。那么递推公式dp[n]=Math.min(dp[n-1]+cost[n-1],dp[n-2]+cost[n-2])。初始化dp[0]=dp[1]=0。填表顺序从左到右。

        完整代码实现:

class Solution {public int minCostClimbingStairs(int[] cost) {int n = cost.length;int[] dp = new int[n + 1];for (int i = 2; i <= n; i++) {dp[i] = Math.min(dp[i - 1] + cost[i - 1], dp[i - 2] + cost[i - 2]);}return dp[n];}
}
  • 第二种解法 

        第一种解法是利用结尾作为状态表示,第二种我们以起点作为状态表示。这里dp[i]表示从i位置出发到达楼顶的最小花费。状态转移方程:当我们支付cost[i]向后走一步到达i+1位置时,再从i+1位置到达终点,花费为dp[i+1]+cost[n];当我们支付cost[i]向后走两步到达i+2位置时,再从i+2位置到达终点,花费为dp[i+2]+cost[n],所以递推公式为dp[i]=Math.min(dp[i+1],dp[i+2])+cost[i]。接着将dp表最后两个元素初始化,仅需支付这一层台阶的花费到达终点即可,所以dp[n-1]=cost[n-1],dp[n-2]=cost[n-2]。填表顺序从左到右。起始位置要从0下标或者1下标开始爬楼梯,最后要返回dp[0]与dp[1]的最小值。

        完整代码实现:

class Solution {public int minCostClimbingStairs(int[] cost) {int n = cost.length;int[] dp = new int[n];// 初始化dp数组的最后一个元素,即到达最后一阶楼梯的花费dp[n - 1] = cost[n - 1];// 初始化dp数组的倒数第二个元素,即到达倒数第二阶楼梯的花费dp[n - 2] = cost[n - 2];for (int i = n - 3; i >= 0; i--) {// 到达当前阶楼梯的最小花费等于到达下一阶或下两阶楼梯的最小花费加上当前阶的花费dp[i] = Math.min(dp[i + 1],dp[i + 2]) + cost[i];}return Math.min(dp[0],dp[1]);}
}

    2.4. 解码方法

            题目给定一个只包含数字的非空字符串,要求计算解码方法的总数。如果没有合法的方式解码整个字符串,返回0。

            状态表示:以i位置为结尾,从0到i计算字符串有多种解码方式,题目当中要求计算总字符串的解码方式,所以dp[i]表示以i位置为结尾,解码方法的总数。状态转移方程:根据最近的一步,让i位置单独解码,或者以i-1和i结合进行解码(我们在算i+1位置时,会考虑i与i+1结合,所以在计算i位置时,不必考虑i与i+1结合)。当i位置单独解码时,1<=i<=9时成功解码,反之失败。如果0到i-1之间的解码数dp[i-1],再加上i位置解码,也是dp[i-1]种,如果失败,则整体解码也失败。当i-1与i位置结合时,10<=10*(i-1)+i<=99才能解码成功,整体解码数为dp[i-2],失败则没有。dp[i]=dp[i-1]+dp[i-2]。初始化:如果0位置解码成功,dp[0]=1,反之dp[1]=1;dp[1]结合上面的推导,可以出现0、1、2三种情况。填表顺序从左到右。结果直接返回dp[i]。

            完整代码实现:

    class Solution {public int numDecodings(String s) {int n = s.length();char[] ch = s.toCharArray();int[] dp = new int[n]; // 创建一个动态规划数组,用于存储每个位置的解码方式数量//初始化第一个位置if (ch[0] != '0') dp[0] = 1;if (n == 1) return dp[0];//初始化第二个位置if (ch[1] != '0' && ch[0] != '0') dp[1] += 1;int t = (ch[0] - '0') * 10 + ch[1] - '0';if (t >= 10 && t <= 26) dp[1] += 1;for (int i = 2; i < n; i++) {//单独解码if (ch[i] != '0') dp[i] += dp[i - 1];//结合解码int tt = (ch[i - 1] - '0') * 10 + ch[i] - '0';if (tt >= 10 && tt <= 26) dp[i] += dp[i - 2];}return dp[n - 1]; // 返回最后一个位置的解码方式数量,即为字符串的总解码方式数量}
    }
    • 边界处理和初始化的优化

            前面三道题的初始化代码非常简单,但这道题的初始化却有很多,并且还存在相似的代码。而接下来就是创建虚拟头结点来对初始化的代码进行优化。我们先将旧的dp表新增一个结点,并将旧dp表里的元素统一后移一位。这里需要注意,1.虚拟结点的值要保证后面填表的正确性,2.下标的映射关系。dp[2]的状态取决于dp[0]和dp[1],根据前面的状态转移方程,需要+dp[0]时,说明0和1位置可以解码成功,所以dp[0]初始化为1,解码不成功直接忽略。当我们在新dp表里面初始化dp[0]时,我需要看字符串0位置的解码情况。所以只需在新dp表里面-1就可以。

            优化后的代码:

    class Solution {public int numDecodings(String s) {int n = s.length();char[] ch = s.toCharArray();int[] dp = new int[n + 1]; // 创建一个动态规划数组,用于存储每个位置的解码方式数量//初始化第一个位置dp[0] = 1;//初始化第二个位置if(ch[1 - 1] != '0') dp[1] = 1;for (int i = 2; i <= n; i++) {//单独解码if (ch[i - 1] != '0')dp[i] += dp[i - 1];//结合解码int tt = (ch[i - 2] - '0') * 10 + ch[i - 1] - '0';if (tt >= 10 && tt <= 26)dp[i] += dp[i - 2];}return dp[n]; // 返回最后一个位置的解码方式数量,即为字符串的总解码方式数量}
    }

    相关文章:

    动态规划算法的欢乐密码(一):斐波那契数模型

    专栏&#xff1a;算法的魔法世界 个人主页&#xff1a;手握风云 目录 一、动态规划 二、例题讲解 2.1. 第 N 个泰波那契数 2.2. 三步问题 2.3. 使用最小花费爬楼梯 2.4. 解码方法 一、动态规划 动态规划是一种将复杂问题分解为更小的子问题&#xff0c;并利用子问题的解来…...

    Echarts柱状图斜线环纹(图形的贴花图案)

    单独设置 <!--此示例下载自 https://echarts.apache.org/examples/zh/editor.html?cbar-stack&codePYBwLglsB2AEC8sDeAoWszGAG0iAXMmuhgE4QDmFApqYQOQCGAHhAM70A0x6L7ACsAjQwtQqhIkwATxDUGbABaMAJsADu9HrAC-xHd3TZqNaCvEHiFcuaKTjAMzAMAzAFIu28hUXPY9ABYPQxIAI2AwTABbV…...

    2025.04.19【Spider】| 蜘蛛图绘制技巧精解

    Basic multi-group radar chart Start with a basic version, learn how to format your input dataset Radar chart with ggradar A Spider chart made using the ggradar package and a lot of customization.A work by Tuo Wang 文章目录 Basic multi-group radar chartRa…...

    机器学习误差图绘

    机器学习误差图绘制 绘图类 # Define the ModelComparisonPlot class class ModelComparisonPlot:def __init__(self, model_name):self.model_name model_namedef plot_comparison(self, y_val, y_pred, mse, mae, r2):# Create a figure with two subplotsfig, axes plt.…...

    llama-factory微调报错:

    报错信息 [INFO] [utils.py:789:see_memory_usage] CPU Virtual Memory: used 81.51 GB, percent 64.9% W0419 10:14:27.573000 108354 site-packages/torch/distributed/elastic/multiprocessing/api.py:897] Sending process 108373 closing signal SIGTERM W0419 10:14:27…...

    【Linux】深入理解Linux文件系统:从C接口到内核设计哲学

    文章目录 前言一、C语言中的文件接口1. 文件指针&#xff08;句柄&#xff09;FILE*以写方式打开文件&#xff0c;若文件不存在会新建一个文件W写入方式&#xff0c;在打开文件之前都会将文件内容全部清空追加写方式&#xff0c;其用法与写方法一致&#xff0c;不同在于a方法可…...

    基于尚硅谷FreeRTOS视频笔记——15—系统配制文件说明与数据规范

    目录 配置函数 INCLUDE函数 config函数 数据类型 命名规范 函数与宏 配置函数 官网上可以查找 最核心的就是 config和INCLUDE INCLUDE函数 这些就是裁剪的函数 它们使用一个ifndef。如果定义了&#xff0c;就如果定义了这个宏定义&#xff0c;那么代码就生效。 通过ifn…...

    Linux网络编程 深入解析TFTP协议:基于UDP的文件传输实战

    知识点1【TFTP的概述】 学习通信的基本&#xff1a;通信协议&#xff08;具体发送上面样的报文&#xff09;、通信流程&#xff08;按照什么步骤发送&#xff09; 1、TFTP的概述 tftp&#xff1a;简单文件传输协议&#xff0c;**基于UDP&#xff0c;**不进行用户有效性验证 …...

    c# MES生产进度看板,报警看板 热流道行业可用实时看生产进度

    MES生产进度看板&#xff0c;报警看板 热流道行业可用实时看生产进度 背景 本软件是给宁波热流道行业客户开发的生产电子看板软件系统 功能 1.录入工艺流程图&#xff08;途程图&#xff09;由多个站别组成。可以手动设置每个工艺站点完成百分比。 2.可以看生成到哪个工…...

    Qt unknown module(s) in qt:serialport解决方法

    在Ubuntu和CentOS系统中,若使用Qt时遇到Unknown module(s) in QT: serialport错误,通常是由于未正确安装Qt的串口模块(QSerialPort)或项目配置不当导致。以下是针对两种系统的解决方案: 一、安装Qt串口模块 1. Ubuntu/Debian系列 安装开发包: 执行以下命令安装Qt5串口模…...

    AtCoder ABC402 A~D 题解

    A - CBC 题目大意 给点字符串 S S S&#xff0c;输出其中所有大写字母。 思路 根据题意模拟即可。 代码 #include <cstdio> #include <iostream> #include <algorithm> using namespace std;int main() {string s;cin >> s;for (int i 0; i &l…...

    2025.04.19-阿里淘天春招算法岗笔试-第二题

    📌 点击直达笔试专栏 👉《大厂笔试突围》 💻 春秋招笔试突围在线OJ 👉 笔试突围OJ 02. 秒杀顺子查找 问题描述 K小姐是一名热爱扑克牌的玩家。她定义一个数列是"顺子",当且仅当将该数列排序后,每个元素恰好比前一个元素大 1 1...

    初识Redis · C++客户端string

    目录 前言&#xff1a; string的API使用 set get&#xff1a; expire: NX XX: mset,mget&#xff1a; getrange setrange: incr decr 前言&#xff1a; 在前文&#xff0c;我们已经学习了Redis的定制化客户端怎么来的&#xff0c;以及如何配置好Redis定制化客户端&…...

    华硕原厂系统枪神9/9p超竟版-WIN11原装开箱出厂系统安装

    华硕原厂系统枪神9/9p超竟版-WIN11-24H2-专业工作站版本安装可带F12-ASUSRecovery恢复功能 适用机型&#xff1a; G635LX、G635LW、G835LX、G835LW、G615LW、G615LP、G615LM、G615LH G815LW、G815LP、G815LM、G815LH、G635LR、G835LR、G615LR、G815LR 远程恢复安装&#xff…...

    CF1016赛后总结

    文章目录 前言T1:Ideal GeneratorT2&#xff1a;Expensive NumberT3:Simple RepetitionT4&#xff1a;Skibidi TableT5:Min Max MEXT6:Hackers and Neural NetworksT7:Shorten the Array 前言 由于最近在半期考试&#xff0c;更新稍微晚了一点&#xff0c;还望大家见谅 &#…...

    QT聊天项目DAY06

    1.从git上同步项目 编译测试&#xff0c;编译通过 Post请求测试 测试成功 2. email is 打印有问题&#xff0c;检查 解析结果是存储在jsonResult中的&#xff0c;修改 3. 客户端实现Post验证码请求 3.1 同步Qt客户端项目 检查QT版本&#xff0c;由于我在公司用的还是QT5.12.9…...

    GNU,GDB,GCC,G++是什么?与其他编译器又有什么关系?

    文章目录 前言1. GNU和他的工具1.1 gcc与g1.2 gdb 2.Windows的Mingw/MSVC3.LLVM的clang/clang4.Make/CMake 前言 在开始之前我们先放一段Hello World&#xff1a;hello.c #include <stdio.h>int main() {printf("Hello World");return 0; }然后就是一段老生常…...

    【AI提示词】IT专家顾问

    提示说明 IT 专家顾问是一位专注于IT行业的专家&#xff0c;拥有深厚的技术背景和广泛的知识储备。他们能够为企业、政府机构或其他组织提供技术支持、解决方案设计和战略规划。 提示词 # Role: IT 专家顾问## Profile - **语言**: 中文 - **描述**: IT 专家顾问是一位专注于…...

    笔记整理五

    STP生成树 stp生成树是用于解决二层环路问题的协议。 二层环路为有以下三种&#xff1a; 1.广播风暴 2.MAC地址的偏移&#xff08;每一次循环&#xff0c;都会导致交换机来回刷新MAC地址表记录&#xff09; 3.多帧复制 stp生成树&#xff1a;需要将原本的环型拓扑结构转换…...

    Java中“this”关键字梳理详解

    在Java中&#xff0c;this 是一个非常重要的关键字&#xff0c;它表示当前对象的引用。也就是说&#xff0c;当你在某个类的实例方法或构造器中时&#xff0c;this 指向调用该方法或创建的当前对象实例。以下将结合代码示例和具体场景&#xff0c;详细讲解 this 的用法及其作用…...

    mybatis plus打印sql日志到指定目录

    1、mybatis plus打印sql日志 参考文档&#xff1a;mybatis plus打印sql日志_mybatisplus日志打印-CSDN博客 2、修改 修改InfoLevelLogger Override public void debug(String s) {// 修改这里logger.info(s);log.debug(s); } 增加&#xff1a;log.debug(s); 修改logback.x…...

    奥比中光tof相机开发学习笔记

    针对奥比中光 tof相机&#xff0c;官方提供的资料如下ProcessOn Mindmap|思维导图 Orbbec SDK Python Wrapper基于Orbbec SDK进行设计封装&#xff0c;主要实现数据流接收&#xff0c;设备指令控制。下面就其开发适配进行如下总结&#xff1a; &#xff08;1&#xff09;系统配…...

    Oracle游标和触发器

    --1.游标 --什么是游标 --游标是数据库在内存中开辟的数据缓冲区 --作用&#xff1a;用于遍历查询返回之后的结果集&#xff08;多条数据结果&#xff09; --游标分类&#xff1a;隐式游标&#xff0c;显示游标&#xff0c;REF游标&#xff08;动态游标&#xff09; --游标的状…...

    【面试向】点积与注意力机制,逐步编码理解自注意力机制

    点积&#xff08;dot product&#xff09;两个向量点积的数学公式点积&#xff08;dot product&#xff09;与 Attention 注意力机制&#xff08;Attention&#xff09;注意力机制的核心思想注意力机制中的缩放点积自注意力机制中&#xff0c;谁注意谁&#xff1f; 逐步编码理解…...

    00.IDEA 插件推荐清单(2025)

    IDEA 插件推荐清单 精选高效开发必备插件&#xff0c;提升 Java 开发体验与效率。 参考来源&#xff1a;十六款好用的 IDEA 插件&#xff0c;强烈推荐&#xff01;&#xff01;&#xff01;不容错过 代码开发助手类 插件名称功能简介推荐指数CodeGeeX智能代码补全、代码生成、…...

    一个 CTO 的深度思考

    今天和一些同事聊了一会&#xff0c;以下是我的观点 我的观点&#xff0c;成年人只能筛选&#xff0c;不能培养在组织中&#xff0c;应该永远向有结果的人看齐。不能当他站出来讲话的时候&#xff0c;大家还要讨论讨论&#xff0c;他虽然拿到结果了&#xff0c;但是他就是有一…...

    MVC/MVVM 高级应用的深度解析

    状态共享与同步 跨组件状态管理策略 状态变更的传播机制优化 状态快照与时间旅行调试 状态持久化 本地存储策略 状态序列化与反序列化 与服务端状态同步 数据绑定进阶 双向绑定优化 脏检查机制优化 基于Proxy/Object.defineProperty的实现差异 批量更新策略 自定义…...

    SQL通用语法和注释,SQL语句分类(DDL,DML,DQL,DCL)及案例

    目录 SQL通用语法和注释 SQL语句分类&#xff08;DDL&#xff0c;DML&#xff0c;DQL&#xff0c;DCL&#xff0c;TPL&#xff0c;CCL&#xff09; DDL&#xff08;数据定义语言&#xff09; 数据库操作 查询&#xff08;SHOW、SELECT&#xff09; 创建&#xff08;CREAT…...

    当算力遇上马拉松:一场科技与肉身的极限碰撞

    目录 一、从"肉身苦修"到"科技修仙" 二、马拉松的"新大陆战争" 三、肉身会被算法"优化"吗? 马拉松的下一站是"人机共生"时代 当AI能预测你的马拉松成绩,算法能规划最佳补给方案,智能装备让训练效率翻倍——你还会用传…...

    AUTOSAR图解==>AUTOSAR_SWS_KeyManager

    AUTOSAR KeyManager详细分析 AUTOSAR 4.4.0 版本密钥与证书管理模块技术分析 目录 1. 概述2. KeyManager架构 2.1 KeyManager在AUTOSAR架构中的位置2.2 架构说明 3. KeyManager模块结构 3.1 模块组件详解3.2 配置项说明 4. KeyManager证书验证流程 4.1 证书验证流程分析 5. Ke…...