【算法】DP系列之 斐波那契数列模型

news2024/12/24 22:10:17

【ps】本篇有 4 道 leetcode OJ。  

目录

一、算法简介

二、相关例题

1)第 N 个泰波那契数

.1- 题目解析

.2- 代码编写

2)三步问题

.1- 题目解析

.2- 代码编写

3)使用最小花费爬楼梯

.1- 题目解析

.2- 代码编写

4)解码方法

.1- 题目解析

.2- 代码编写


一、算法简介

        动态规划(Dynamic Programming)算法的核心思想是:将大问题划分为小问题进行解决,从而一步步获取最优解的处理算法

        动态规划算法与分治算法类似,其基本思想也是将待求解问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解。

        与分治法不同的是,适合于用动态规划求解的问题,经分解得到的子问题往往不是互相独立的(即下一个子阶段的求解是建立在上一个子阶段的解的基础上)。

【Tips】动态规划算法解决问题的分类

  • 计数:有多少种方式走到右下角 / 有多少种方法选出k个数使得和是 sum。
  • 求最大值/最小值:从左上角走到右下角路径的最大数字和最长上升子序列长度。
  • 求存在性:取石子游戏,先手是否必胜 / 能不能取出 k 个数字使得和是 sum。

【Tips】动态规划dp算法一般步骤

  1. 确定状态表示(dp[ i ] 的含义是什么,来源:1、题目要求;2、经验+题目要求;3、分析问题时发现重复子问题)
  2. 状态转移方程(可求得 dp[ i ] 的数学公式,来源:题目要求+状态表示)
  3. 初始化(dp 表中特别的初始值,保证填 dp 表时不会越界,来源:题目要求+状态表示)
  4. 填表顺序(根据状态转移方程修改 dp[ i ] 的方式,来源:题目要求+状态表示)
  5. 返回值(题目求解的结果,来源:题目要求+状态表示)

二、相关例题

1)第 N 个泰波那契数

1137. 第 N 个泰波那契数

.1- 题目解析

        由题,题目的公式其实可以变形,从而得到如下公式:

        由题,dp[i] 的含义可以是第 i 个泰波那契数的值,由此可得状态转移方程即为泰波那契数的公式:dp[i] = dp[i-1] + dp[i-2] + dp[i-3]。

        特别的,第 0 个泰波那契数为 0,第 1 个和第 2 个泰波那契数为 1,也就是说 dp 表要做相应的初始化,即 dp[0] = 0、dp[1] = 1、dp[2] = 1。

        在根据状态转移方程填 dp 表时,需要用到已经计算过的状态,那么填表顺序就是从左往右依次填表。

        返回值则为第 N 个泰波那契数,即 dp[n]。

【补】滚动数组思想进行空间优化

        假设有 a、b、c、d 四个变量,分别对应了泰波那契数列的前四个数,要求得第 n 个泰波那契数,只需让这四个变量按照如下方式,不断进行赋值操作即可:

  • d = a + b + c;
  • a = b;
  • b = c;
  • c = d;

        在 a、b、c、d 不断向数列后面移动的过程中,这四个变量组成的一个序列就在数列中整体向后遍历。

.2- 代码编写

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];
    }
};
class Solution {
public:
    int tribonacci(int n) {
            if(n <= 1) // 处理边界
                return n;
 
            int a = 0, b = 1, c = 1, d = 1; // 滚动数组思想优化空间
            for(int i = 3; i <= n; ++i)
            {
                d = a + b + c;
                a = b;
                b = c;
                c = d;
            }
            return d;
    }
};

2)三步问题

面试题 08.01. 三步问题

.1- 题目解析

        以示例 1 为例,从楼梯下(即第 0 阶)上到第 1 阶,跨出一步即可,也就只有一种方式;

到第 2 阶可以一步一步地跨,也可以一次性跨两步,就有两种方式;到第 3 阶,可以一步一步跨,也可以一次性跨三步,还可以先跨一步再跨两边,或先跨两步再跨一步,一共有四种方式;到第 4 阶可以一步一步跨,或两步两步地跨,也可以先跨一步再跨三步,或先跨三步再跨一步,还可以先跨一步再跨一步再跨两步,或先跨一步再跨两步再跨一步,或先跨两步再跨一步再跨一步,一共有七种方式......以此类推,不难发现,从第 3 阶开始,相邻三阶各自的方式之和是下一阶的方式。

       由此,dp[i] 的含义可以是上到第 i 阶的方式,而状态转移方程即为:dp[i] = dp[i-1] + dp[i-2] + dp[i-3]。

        特别的,上到第 1 阶的方式为 1,第 2 阶的方式为 2,第 3 阶的方式为 4,也就是说 dp 表要做相应的初始化,即 dp[0] = 0、dp[1] = 1、dp[2] = 2、dp[3] = 4。

        在根据状态转移方程填 dp 表时,需要用到已经计算过的状态,那么填表顺序就是从左往右依次填表。

        返回值则为上到第 N 阶的方式,即 dp[n]。

.2- 代码编写

class Solution {
public:
    int waysToStep(int n) {
        if(n==1 ||n==2)return n;
        if(n==3)return 4;

        const int MOD=1e9+7;//按题目要求特殊处理结果
        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])%MOD+dp[i-3])%MOD;//按题目要求特殊处理结果
        return dp[n];
    }
};

3)使用最小花费爬楼梯

746. 使用最小花费爬楼梯

.1- 题目解析

        如果 dp[ i ] 表示到达 i 位置的最小花费,则到达dp[ i ] 就有以下两种情况:

  • 先到达dp[ i-1 ] ,然后支付cost[ i-1] 到达 dp[ i ]。
  • 先到达dp[ i-2 ] ,然后支付cost[ i-2] 到达 dp[ i ]。

        题意及取两种情况小的那个,则得到状态转移方程:dp[i] =min(dp[i-1] + cost[i-1], dp[i-2] + cost[i-2])。此外,填表顺序为从左往右,要将第 0 阶和第 1 阶的花费初始化为 0,即dp[0]=0,dp[1]=0,返回值则为 dp[n]。


        而如果 dp[ i ] 表示从 i 位置出发,到达楼顶,此时的最小花费,则dp[ i ] 就有以下两种情况:

  • 支付 cost[ i ],往后走一步,从 i + 1的位置出发到终点。
  • 支付 cost[ i ],往后走两步,从 i + 2的位置出发到终点。

        则需要知道 i + 1 和 i + 2 位置的最小花费,及dp [i + 1] 和 dp[i + 2],那么就需要从右往左填,且状态转移方程为:dp[i] = min(dp[i+1] + cost[i], dp[i+2] + cost[i])。特别的,由于需要用到后两个已求得的状态来求当前状态,因此 dp 表的结尾两个位置就需要初始化,即 dp[n-1] = cost[n-1],dp[n-2] = cost[n-2];且返回值为 dp[0] 和 dp[1] 中的较小者。

.2- 代码编写

//dp[ i ] 表示到达 i 位置的最小花费
class Solution {
public:
    int minCostClimbingStairs(vector<int>& cost) {
        int n=cost.size();
        vector<int> dp(n+1);
        dp[0]=dp[1]=0;
        for(int i=2;i<=n;i++)   
            dp[i]=min(dp[i-2]+cost[i-2],dp[i-1]+cost[i-1]);

        return dp[n];
    }
};
//dp[ i ] 表示从 i 位置出发,到达楼顶,此时的最小花费
class Solution {
public:
    int minCostClimbingStairs(vector<int>& cost) {
        int n=cost.size();
        vector<int> dp(n+1);
        dp[n-1]=cost[n-1],dp[n-2]=cost[n-2];
        for(int i=n-3;i>=0;i--)
            dp[i]=cost[i]+min(dp[i+1],dp[i+2]);
        return min(dp[0],dp[1]);
    }
}; 

4)解码方法

91. 解码方法

.1- 题目解析

        dp[i] 可以表示,以 i 位置为结尾时,解码方法的总数。

        关于 i 位置的编码状况,可以分为下面两种情况:

  • 让 i 位置上的数单独解码成一个字母
  • 让 i 位置上的数与 i - 1 位置上的数结合,解码成一个字母

        如此,让 i 位置上的数单独解码成一个字母,就存在 解码成功 和 解码失败 两种情况:

  • 解码成功:dp[i] = dp[i - 1],在 [0,i] 上所有解码结果后面填上一个字母即可。
  • 解码失败:dp[i] = 0,前面努力都白费。

        而让 i 位置上的数与 i - 1 位置上的数结合在⼀起,解码成一个字母,同样也存在解码成功和解码失败两种情况:

  • 解码成功:dp[i] = dp[i - 2],在 [0,i] 上所有解码结果后面填上一个字母即可。
  • 解码失败:dp[i] = 0,前面努力都白费。

        综上,以 i 位置为结尾时解码方法的总数即为 dp[i] = dp[i - 1] + dp[i - 2],填表顺序为从左往右。

【补】处理边界情况和初始化问题的技巧

.2- 代码编写

class Solution {
public:
    int numDecodings(string s) {
        int n=s.size();
        vector<int> dp(n);
        dp[0]=s[0]!='0';
        if(n==1)return dp[0];

        if(s[0]!='0' && s[1]!='0')dp[1]+=1;
        int t=(s[0]-'0')*10+s[1]-'0';
        if(t>=10 && t<=26)dp[1]+=1;

        for(int i=2;i<n;i++)
        {
            if(s[i]!='0')dp[i]+=dp[i-1];//处理单独编码的情况
            int t=(s[i-1]-'0')*10+s[i]-'0';//处理结合编码的情况
            if(t>=10 && t<=26)dp[i]+=dp[i-2];
        }
        return dp[n-1];
    }
};
class Solution {
public:
    int numDecodings(string s) {
        int n=s.size();
        vector<int> dp(n+1);
        dp[0]=1;
        dp[1]=s[1-1]!='0';
        
        for(int i=2;i<=n;i++)
        {
            if(s[i-1]!='0')dp[i]+=dp[i-1];
            int t=(s[i-2]-'0')*10+s[i-1]-'0';
            if(t>=10 && t<=26)dp[i]+=dp[i-2];
        }
        return dp[n];
    }
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2203861.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

vue3实现 长列表虚拟滚动

1、直接看代码 <template><!--定义一个大容器&#xff0c;此容器可以滚动--><div class"view" ref"viewRef" scroll"handleScroll"><!--定义一个可以撑满整个data的容器&#xff0c;主要是让父元素滚动起来--><div …

EasyExcel读入数字类型数据时出现小数位丢失精度问题

这里写自定义目录标题 问题现象解决方案 问题现象 目前使用easyExcel读取导入文档时发现文档中的小数值4076204076.65会被读取为4076204076.6500001 尝试去查看了excel解压后的文件&#xff0c;发现这条数据在xml里存储的值就是4076204076.6500001&#xff0c;即是excel存储小…

java中的I/O(8个案例+代码+效果图)

目录 1.File类 1&#xff09;常用构造方法 1&#xff09;File(String pathname) 2&#xff09;File(String parent, String child) 3&#xff09;File(File parent, String child) 2&#xff09;常用方法 1&#xff09;boolean canRead() 2&#xff09;boolean canWrite() 3&am…

诺奖现场采访2024物理学得主Hinton:当前AI革命堪比工业革命,且将在智力上全面超越人类

当地时间昨天&#xff0c;2024年10月8日&#xff0c;瑞典皇家科学院宣布将本年度诺贝尔物理学奖授予两位被誉为AI教父的科学家&#xff1a;约翰J霍普菲尔德&#xff08;John J. Hopfield&#xff09;和杰弗里E辛顿&#xff08;Geoffrey E. Hinton&#xff09;。该奖项旨在表彰他…

Leetcode 买卖股票的最佳时机

这段代码的目的是解决“买卖股票的最佳时机”这个问题&#xff0c;即在给定的股票价格数组中&#xff0c;找到一次买入和卖出所能获得的最大利润。 算法思想&#xff1a; 定义两个变量&#xff1a; minPrice: 这个变量用于记录迄今为止遇到的最小股票价格&#xff08;买入价格…

软件项目必须进行验收测试吗?专业验收测试报告如何获取?

软件项目验收测试是一种关键的质量保证活动&#xff0c;旨在确保软件产品符合用户需求和预期功能。它通常是在软件开发完成后&#xff0c;由客户或第三方测试机构进行的最终测试环节。验收测试的目的是确认软件的性能、功能、安全性和其他特性&#xff0c;以确保交付的产品能够…

【开源免费】基于SpringBoot+Vue.JS医院电子病历管理系统(JAVA毕业设计)

本文项目编号 T 008 &#xff0c;文末自助获取源码 \color{red}{T008&#xff0c;文末自助获取源码} T008&#xff0c;文末自助获取源码 目录 一、系统介绍二、演示录屏三、启动教程四、功能截图五、文案资料5.1 选题背景5.2 国内外研究现状5.3 可行性分析 六、核心代码6.1 医…

5G NR UE初始接入信令流程

文章目录 5G NR UE初始接入信令流程 5G NR UE初始接入信令流程 用户设备向gNB-DU发送RRCSetupRequest消息。gNB-DU 包含 RRC 消息&#xff0c;如果 UE 被接纳&#xff0c;则在 INITIAL UL RRC MESSAGE TRANSFER 消息中包括为 UE 分配的低层配置&#xff0c;并将其传输到 gNB-CU…

RK3568启动配置

硬件环境 连上电脑&#xff0c;这时候默认是uboot启动linux&#xff0c;因此我们需要进入uboot的命令行界面&#xff0c;通过网络加载oneos运行。启动后按空格&#xff1a; Rockchip watchdog timeout: 15 sec Net: eth0: ethernetfe2a0000, eth1: ethernetfe010000 Hit key…

视频去水印的3款软件:助你轻松一键去水印

在视频创作的世界里&#xff0c;水印往往是一个让人头疼的问题。无论是从网络上下载的素材&#xff0c;还是其他平台分享的视频&#xff0c;都可能带有水印&#xff0c;影响整体美观。今天&#xff0c;就为大家介绍三款视频去水印软件&#xff0c;它们分别是影忆、Online Water…

数据结构-二叉树_堆

一. 树的概念 树在我们的日常生活中随处可见&#xff0c;人们将生活中的树转换成存放数据的树形结构&#xff0c;就成了数据结构中的“树”。 如上图所示&#xff0c;自然界中的树有树根&#xff0c;有树枝&#xff0c;有树叶&#xff0c;当我们将其转换成树形结构时&#xf…

ModBus Pull的详细安装教程

目录 一.导航 二 .安装 三.激活 四.使用 一.导航 modbus poll 和 modbus slave 是两种Modbus协议的软件工具 。 Modbus Poll&#xff1a;Modbus Poll 是一个客户端&#xff08;或主站&#xff09;软件&#xff0c;它允许用户与支持Modbus协议的设备进行通信。 Modbus Sla…

英国毕业论文问卷及采访设计思路解析

在大多数情况下&#xff0c;英国毕业论文都需要学生收集一手资料。而在一手资料的收集过程中&#xff0c;学生可以通过设计试验&#xff0c;观察&#xff0c;问卷和采访等方式从目标人群或者实验人群中收集到所需的一手数据。在本文中&#xff0c;我们将针对商科和教育学之类的…

Linux——echo-tail-重定向符

echo命令 类似printf 输出 反引号 重定向符 > 和 >> > 覆盖 >> 追加 tail命令 查看文件尾部内容&#xff0c;追踪文件最新更改 tail -num 从尾部往上读num行&#xff0c;默认10行 tail -f 持续跟踪

使用 Go 语言与 Redis 构建高效缓存与消息队列系统

什么是 Redis&#xff1f; Redis 是一个开源的内存数据库&#xff0c;支持多种数据结构&#xff0c;包括字符串、列表、集合、哈希和有序集合。由于 Redis 运行在内存中&#xff0c;读写速度极快&#xff0c;常被用于构建缓存系统、实时排行榜、会话存储和消息队列等高并发场景…

论文《OneLLM:One Framework to Align All Modalities with Language》

&#xff08;没有会员只有做100个节点&#xff0c;mindmaster金主爸爸可不可以给我一个会员啊啊啊啊呜呜呜~&#xff09; 欣赏论文的图和表&#xff1a; 表中作者将自己的模型那一行选择灰色作为背景&#xff0c;更加凸显自己的数据&#xff0c;另外对于最好的结果用加粗黑体…

threads_created增加过大?

set global thread_cache_size128; 在my.cnf文件中直接加上thread_cache_size128 原值为58 MySQL中的Threads线程相关指标解读 - SRE Work mysql性能优化(thread_created) - 八戒vs - 博客园 MySQL :: MySQL 8.4 Reference Manual :: 7.1.10 Server Status Variables

基于Vue3+axios+element-plus等技术开发的新闻发布管理系统

新闻发布管理系统是一个基于Vue3piniavue-routeraxioselement-plus等开发的系统&#xff0c;主要功能包括&#xff1a;登录模块、注册模块、新闻分类管理模块、新闻管理模块、个人中心模块&#xff08;包括基本资料、更换头像、重置密码功能&#xff09;等。 代码下载&#xf…

凡事预则立,不预则废

在交易的竞技场上&#xff0c;每位交易员都拥有自己的一套打法&#xff0c;这些独特的交易风格不仅塑造了他们的个人特点&#xff0c;更是他们成功的关键所在。今天采访的Eagle Trader交易员刘先生&#xff0c;就给我一种很稳妥的感觉&#xff0c;那么&#xff0c;刘先生的“稳…

Linux使用Docker部署Paperless-ngx结合内网穿透打造无纸化远程办公

文章目录 前言1. 部署Paperless-ngx2. 本地访问Paperless-ngx3. Linux安装Cpolar4. 配置公网地址5. 远程访问6. 固定Cpolar公网地址7. 固定地址访问 前言 本文主要介绍如何在Linux系统本地部署Paperless-ngx开源文档管理系统&#xff0c;并结合cpolar内网穿透工具解决本地部署…