动态规划 之 斐波那契数列模型 算法专题

news2024/12/6 0:54:22

动态规划

分析:(五步)

  1. 状态表示

状态表示是什么?
dp表里面的值锁表示的含义

状态表示怎么来的?

  • 题目要求
  • 经验 + 题目要求
  • 线性状态表经验: 以i位置为结尾 以i位置为起点
  • 分析问题的过程中, 发现重复子问题
  1. 状态转移方程
    dp[i] 等于什么(一个公式)
    用之前或者之后的状态, 推导出dp[i]的值
  2. 初始化
    保证填表的时候不越界
  3. 填表顺序
    为了填写当前状态的时候, 所需要的状态已经计算过了
  4. 返回值
    题目要求 + 状态表示

编写代码: (四步)

  1. 创建dp表
  2. 初始化
  3. 填表
  4. 确定返回值

优化: 处理边界问题以及初始化问题的技巧
如果初始化和填表的逻辑类似, 我们可以把初始化塞到填表逻辑中
做法:
dp多开一个空间, 将之前的dp表整体向后移动, 前面的一个位置称为虚拟节点
注意:
1.虚拟结点里面的值, 要保证是正确的 一般情况下设置为0, 但还是要具体分析
2.下标的映射关系

一.第n个泰波那契数

第n个泰波那契数

  1. 状态表示
    dp表 表示第n个泰波那契数
  2. 状态转移方程
    dp[i] = dp[i - 3] + dp[i - 2] + dp[i - 1]
  3. 初始化
    dp[0] = 0 dp[1] = 1 dp[2] = 1
  4. 填表顺序
    从左往右
  5. 返回值
    返回dp[n]
class Solution {
    public int tribonacci(int n) {

        //处理边界情况
        if(n == 0) return 0;
        if(n == 1 || n == 2) return 1;

        //1. 创建dp表
        int[] dp = new int[n + 1];
        //2. 初始化
        dp[0] = 0;
        dp[1] = dp[2] = 1;
        //3. 填表
        for(int i = 3; i <= n; i++){
            //状态转移方程
            dp[i] = dp[i - 3] + dp[i - 2] + dp[i - 1];
        }
        //4. 确定返回值
        return dp[n];
    }
}

空间优化: 用滚动数组来实现(此题使用变量即可)

class Solution {
    public int tribonacci(int n) {

        //处理边界情况
        if(n == 0) return 0;
        if(n == 1 || n == 2) return 1;

        //空间优化
        int a = 0, b = 1, c = 1, d = 0;
        for(int i = 3; i <= n; i++){
            d = a + b + c;
            a = b;
            b = c;
            c = d;
        }
        //确定返回值
        return d;
    }
}

二. 三步问题

三步问题

  1. 状态表示
    dp[i] 表示到达i位置时, 一共有多少种方法
  2. 状态转移方程
    以i位置的状态, 最近的一步来划分问题
    1.从 i - 1 到 i dp[i - 1]
    2.从 i - 2 到 i dp[i - 2]
    3.从 i - 3 到 i dp[i - 3]
    dp[i] = dp[i - 3] + dp[i - 2] + dp[i - 1]
  3. 初始化
    dp[1] = 1 dp[2] = 1 dp[3] = 4
  4. 填表顺序
    从左往右
  5. 返回值
    返回dp[n]
class Solution {
    public int waysToStep(int n) {
        // 1. 创建dp表
        // 2. 初始化
        // 3. 填表
        // 4. 确定返回值
        
        int MOD = (int)1e9 + 7;

        if(n == 1 || n == 2) return n;
        if(n == 3) return 4;

        int[] dp = new int[n + 1];
        dp[1] = 1;
        dp[2] = 2;
        dp[3] = 4;
        for(int i = 4; i <= n; i++){
            dp[i] = ((dp[i - 3] + dp[i - 2]) % MOD + dp[i - 1]) % MOD;//每次做加法都有可能越界
        }
        return dp[n];
    }
}

三. 使用最小花费爬楼梯

使用最小花费爬楼梯
方法一: 以i位置结尾

  1. 状态表示
    dp[i] 表示到达i位置时, 最小花费
  2. 状态转移方程
    以i位置的状态, 最近的一步来划分问题
    1.先到达 i - 1 的位置, 然后支付cost[i - 1] 走一步 到 i dp[i - 1] + cost[i - 1]
    2.从先到达 i - 2 的位置, 然后支付cost[i - 2] 走两步 到 i dp[i - 2] + cost[i - 2]
  • dp[i] =min(dp[i - 1] + cost[i - 1], dp[i - 2] + cost[i - 2])
  1. 初始化
    dp[0] = dp[1] = 0
  2. 填表顺序
    从左往右
  3. 返回值
    返回dp[n]
class Solution {
    public int minCostClimbingStairs(int[] cost) {
        // 1. 创建dp表
        // 2. 初始化
        // 3. 填表
        // 4. 确定返回值

        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];
    }
}

方法二: 以i位置开始

  1. 状态表示
    dp[i] 表示从i位置, 到达顶点的最小花费
  2. 状态转移方程
    以 i 位置的状态, 最近的一步来划分问题
    1.先支付cost[i] 走一步, 从 i 位置到达 i + 1 的位置, 然后到终点 dp[i + 1] + cost[i]
    2.先支付cost[i] 走两步, 从 i 位置到达 i + 2 的位置, 然后到终点 dp[i + 2] + cost[i]
  • dp[i] =min(dp[i + 1] + cost[i], dp[i + 2] + cost[i])
  1. 初始化
    dp[n] = cost[n], dp[n - 1] = cost[n - 1]
  2. 填表顺序
    从右往左
  3. 返回值
    返回min(dp[0], dp[1])
class Solution {
    public int minCostClimbingStairs(int[] cost) {
        // 1. 创建dp表
        // 2. 初始化
        // 3. 填表
        // 4. 确定返回值
        int n = cost.length;
        int[] dp = new int[n];
        dp[n - 2] = cost[n - 2];
        dp[n - 1] = cost[n - 1];
        for(int i = n - 3; i >= 0; i--){
            dp[i] = Math.min(dp[i + 1] + cost[i], dp[i + 2] + cost[i]);
        }
        return Math.min(dp[0], dp[1]);
    }
}

四. 解码方法

解码方法

  1. 状态表示
    dp[i] 表示以 i 位置结尾, 解码方法的总数
  2. 状态转移方程

1.只解码 i 位置, 可以分成成功和失败两种情况
成功, s[i] 一定是>= 1 && <= 9, 说明从头开始到i位置, 都是可以正常解码的, 只需在解码成功的字符串后面加上一个字符, 那么dp[i] = dp[i - 1]
失败 说明不管前面解码是否成功, i位置失败了, 就不算解码成功, dp[i] = 0
2.解码 i 位置和 i - 1 位置, 也可以分成成功和失败两种情况
成功, s[i - 1] * 10 + s[i] 一定是>= 10 && <= 26, 说明从头开始到i位置, 都是可以正常解码的, 只需在解码成功的字符串(dp[i - 2])后面加上一个字符, 那么dp[i] = dp[i - 2]
失败 说明不管前面解码是否成功, i位置失败了, 就不算解码成功, dp[i] = 0

  • 成功条件: dp[i] =dp[i - 1] + dp[i - 2]
  1. 初始化
    dp[0] 一个字符的解码总数: 成功为1, 失败为0
    dp[1] 一个字符: 成功为1, 失败为0, 两个字符: 成功为1, 失败为0, 所以dp[1]可能为1, 2, 0
  2. 填表顺序
    从左往右
  3. 返回值
    返回dp[n - 1]
class Solution {
    public int numDecodings(String ss) {
        // 1. 创建dp表
        // 2. 初始化
        // 3. 填表
        // 4. 返回值

        char[] s = ss.toCharArray();
        int n = s.length;
        int[] dp = new int[n];
        //初始化dp[0]
        if (s[0] != '0')
            dp[0] += 1;
        //注意字符长度
        if (n == 1)
            return dp[0];
        //初始化dp[1]
        if (s[1] != '0' && s[0] != '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 tt = (s[i - 1] - '0') * 10 + s[i] - '0';
            if (tt >= 10 && tt <= 26)
                dp[i] += dp[i - 2];
        }
        return dp[n - 1];
    }
}

优化:
此题中, 虚拟节点dp[0]中的值, 应该为1
因为当dp[1]和dp[2]两个字符一起看的时候, 如果能映射成字符, 那么dp[2] = dp[2 - 2] = dp[0], 此时应该算作一个结果, 应该为1

class Solution {
    public int numDecodings(String ss) {
        // 1. 创建dp表
        // 2. 初始化
        // 3. 填表
        // 4. 返回值

        char[] s = ss.toCharArray();
        int n = s.length;
        int[] dp = new int[n + 1];
        // 初始化dp[0]
        dp[0] = 1;// 虚拟节点
        // 初始化dp[1]
        if (s[0] != '0')
            dp[1] = 1;

        // 填表
        for (int i = 2; i <= n; i++) {

            if (s[i - 1] != '0')
                dp[i] += dp[i - 1];

            int tt = (s[i - 2] - '0') * 10 + s[i - 1] - '0';
            if (tt >= 10 && tt <= 26)
                dp[i] += dp[i - 2];
        }
        return dp[n];
    }
}

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

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

相关文章

微积分复习笔记 Calculus Volume 1 - 4.8 L’Hôpital’s Rule

4.8 L’Hpital’s Rule - Calculus Volume 1 | OpenStax

用户流定义:绘制产品交互流程图

产品经理在进行产品设计时&#xff0c;经常利用交互流程图来提升团队的工作效率。这种流程图适用于传达方案、评审目标等需要团队协作的场景&#xff0c;使得视觉设计师、产品开发等团队成员能够迅速理解图示内容&#xff0c;节省了理解时间&#xff0c;有效提高了沟通效率。 …

Linux -- 冯诺依曼体系结构(硬件)

目录 概念 五大组成部分 为什么需要存储器&#xff1f; 计算机存储金字塔层状结构 为什么程序需要加载到内存中 概念 冯诺依曼体系结构是以数学家冯诺依曼的名字命名的一种计算机体系结构。这种体系结构是现代计算机的基础&#xff0c;它定义了计算机的基本组件及其相互…

群控系统服务端开发模式-应用开发-本地上传工厂及阿里云上传工厂开发

记住业务流程图&#xff0c;要不然不清楚自己封装的是什么东西。 一、本地工厂开发 在根目录下extend文件夹下Upload文件夹下channel文件夹中&#xff0c;我们修改LocalUpload业务控制器。具体代码如下&#xff1a; <?php /*** 本地上传工厂* User: 龙哥 三年风水* Date: …

鹧鸪云光伏小程序上线啦

为了适应市场的发展需求&#xff0c;现推出了手机端SaaS版的光伏小程序&#xff0c;里面包含很多免费的小工具&#xff0c;供给我们业务人员、施工人员方便手机上操作&#xff0c;省去了带着电脑的笨重。下面给大家介绍下里面的免费小工具。 功率的换算&#xff1a;这里主要计…

WireShark入门学习笔记

学习视频&#xff1a;WireShark入门使用教程 扩展学习&#xff1a;wireshark分析常见的网络协议 文章目录 WireShark介绍WireShark抓包入门操作WireShark过滤器使用WireShark之ARP协议分析WireShark之ICMP协议TCP连接的3次握手协议TCP连接断开的4次挥手协议WireShark抓HTTP协…

人工智能之人脸识别(人脸采集人脸识别)

文章目录 前言PySimpleGUI 库1-布局和窗口2 文本框组件3-视频处理图片处理数据库操作数据采集&#xff08;重要部分&#xff09;人脸识别&#xff08;综合部分&#xff09; 前言 例如&#xff1a;随着人工智能的不断发展&#xff0c;本文主要介绍关于人工智能中GUI和PyMysql相…

qt5将程序打包并使用

一、封装程序 (1)、点击创建项目->库->clibrary &#xff08;2&#xff09;、填写自己想要封装成库的名称&#xff0c;这里我填写的名称为mydll1 &#xff08;3&#xff09;、如果没有特殊的要求&#xff0c;则一路下一步&#xff0c;最终会出现如下文件列表。 (4)、删…

通用方式创建未知文件后缀文件

困惑&#xff1a;比如平时想创一个类似&#xff1a;Dockerfile 文件如何玩&#xff1f; entrypoint.sh 如何玩&#xff1f; windows平台&#xff0c;直接命令行&#xff1a; mac平台或者linux平台也类似

swiper分页器自定义

实现&#xff1a; <template><div class"center-top-swiper"mouseenter"on_bot_enter"mouseleave"on_bot_leave"><swiper :options"swiperOption"ref"mySwiper"><swiper-slide v-for"i in 4&quo…

2025 年使用 Python 和 Go 解决 Cloudflare 问题

作为一名从事网络自动化和爬取工作的开发者&#xff0c;我亲眼目睹了日益复杂的安全性措施带来的挑战。其中一项挑战是 Cloudflare 的 Turnstile CAPTCHA 系统&#xff0c;目前该系统已在全球 2600 多万个网站上使用。这种先进的解决方案重新定义了我们对机器人检测的处理方式&…

windows自启动 映像劫持 屏保

Windows权限维持—自启动&映像劫持&粘滞键&辅助屏保后门 自启动 自启动路径加载 受控windows机器选择当前用户C盘目录下将文件放到这里每到电脑服务器重启就会自动加这次路径下文件 C:\Users\月\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startu…

OTA篇(1)AB系统

制作升级包&#xff1a; 一、整包升级包制作 以tina4.0 A133 b3版型为例 在/target/allwinner/a133-b6/swupdate或者 target/allwinner/generic/swupdate/目录添加如下文件 1.非安全固件 sw-subimgs-ab.cfg swota_file_list( target/allwinner/generic/swupdate/sw-descr…

移远通信推出八款天线新品,覆盖5G、4G、Wi-Fi和LoRa领域

近日&#xff0c;全球领先的物联网整体解决方案供应商移远通信宣布&#xff0c;再次推出八款高性能天线新品&#xff0c;进一步丰富其天线产品阵容&#xff0c;更好地满足全球客户对高品质天线的更多需求。具体包括5G超宽带天线YECT005W1A和YECT004W1A、5G天线YECT028W1A、4G天…

AI时代,中国高端厨居生活还能怎样进化?

每次走进厨房&#xff0c;看到安静待在角落的各式各样厨电和琳琅满目的食材&#xff0c;想想刚从职场卸甲归来&#xff0c;却还要和这些东西斗智斗勇&#xff0c;都忍不住来上一句&#xff1a;要是有魔法就好了。 有了魔法就能像《哈利波特》里的韦斯莱夫人一样&#xff0c;只…

【HTML】——VSCode 基本使用入门和常见操作

阿华代码&#xff0c;不是逆风&#xff0c;就是我疯 你们的点赞收藏是我前进最大的动力&#xff01;&#xff01; 希望本文内容能够帮助到你&#xff01;&#xff01; 目录 零&#xff1a;HTML开发工具VSCode的使用 1&#xff1a;创建项目 2&#xff1a;创建格式模板&#x…

ssm057学生公寓管理中心系统的设计与实现+jsp(论文+源码)_kaic

毕 业 设 计&#xff08;论 文&#xff09; 题目&#xff1a;学生公寓管理中心系统设计与实现 摘 要 现代经济快节奏发展以及不断完善升级的信息化技术&#xff0c;让传统数据信息的管理升级为软件存储&#xff0c;归纳&#xff0c;集中处理数据信息的管理方式。本学生公寓管理…

WAL日志

1.WAL概述 PG WAL&#xff08;Write-Ahead Logging&#xff09;日志是PostgreSQL数据库中的一种重要机制&#xff0c;用于保证数据库的完整性和数据恢复。 1.1定义与功能 WAL日志是PostgreSQL的持久性技术&#xff0c;它将所有对数据库的修改操作&#xff08;如INSERT、UPDA…

算法练习:1658. 将 x 减到 0 的最小操作数

题目链接&#xff1a;1658. 将 x 减到 0 的最小操作数 这道题目的意思就是&#xff0c;给定一个整数数组&#xff0c;和一个x&#xff0c;只能从数组最左边或者最右边进行删除&#xff0c;使得x恰好等于0&#xff0c;并且要操作次数最少的情况&#xff0c;否则返回-1. 这道题直…

51c大模型~合集17

我自己的原文哦~ https://blog.51cto.com/whaosoft/11599989 #关于大模型「越狱」的多种方式 此项目是由伊利诺伊大学香槟分校&#xff08;UIUC&#xff09;的汪浩瀚教授主导&#xff0c;汇集了多名intern的共同努力而成。长久以来&#xff0c;这个跨学科的团队一直在前沿科…