【算法】动态规划-斐波那契模型

news2024/11/27 10:36:34

文章目录

  • 结论
  • 斐波那契模型
    • 第 N 个泰波那契数
    • 三步问题
    • 使用最小花费爬楼梯
      • **方法1:**以i位置为结尾....
      • 方法2:以i位置为起点....
    • 解码方法

结论

对于线性dp,一般是用经验+题目要求来定义状态表示:

  • 以某个位置为结尾…
  • 以某个位置为起点…。

斐波那契模型

第 N 个泰波那契数

https://leetcode.cn/problems/n-th-tribonacci-number/

image-20230606101650677

分析

1.状态表示:

  • dp[i]:表示第i个泰波那契数的值

2.状态转移方程分析:

  • image-20230606102018189

  • 所以状态转移方程为:dp[i] = dp[i - 1] + dp[i - 2] + dp[i - 3]

3.初始化< ==>目的是为了防止填dp表的时候不越界

  • 当前位置的值依赖的是前面3个位置的值,所以需要初始化dp[0],dp[1],dp[2],题目已经给出T0,T1,T2的值

4.填表顺序

  • 从第三个位置开始,从左往右填表

5.返回值

  • 求的是第 n 个泰波那契数的值,所以返回:dp[n]
class Solution {
public:
    int tribonacci(int n) {
        if(n == 0 || n == 1) return n;
        if(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];
    }
};

优化:使用滚动数组优化

image-20230606103315852

class Solution {
public:
    int tribonacci(int n) {
        if(n == 0 || n == 1) return n;
        if(n == 2) return 1;
        //a:dp[i-3] b:dp[i-2] c:dp[i-1]
        int a  = 0,b = 1,c = 1,d = 0;
        for(int i = 3;i<=n;i++)
        {
            d = a + b + c;
            //a b c三者赋值
            a = b;
            b = c;
            c = d;
        }
        return d;
    }
};

三步问题

https://leetcode.cn/problems/three-steps-problem-lcci/

image-20230606103415961

分析

1.状态表示

  • dp[i]:到达i位置的时候,一共有多少种方法

2.状态转移方程 : i i i位置状态的最近的⼀步,来分情况讨论:

  • 可以从三个位置来到当前位置
    • 1.从i-1位置跳1阶台阶来到当前位置 此时的方法数为dp[i-1]
    • 2.从i-2位置跳2阶台阶来到当前位置 此时的方法数为dp[i-2]
    • 3.从i-3位置跳3阶台阶来到当前位置 此时的方法数为dp[i-3]

image-20230606103837401

**注意:**由于结果可能很⼤,需要对结果取模。

对于这类需要取模的问题,我们每计算⼀次(两个数相加/乘等),都需要取⼀次模,否则可能发⽣了溢出

//err写法: (dp[i - 1] + dp[i - 2] + dp[i - 3])  % MOD
//正确写法   
dp[i] = ((dp[i - 1] + dp[i - 2]) % MOD + dp[i - 3]) % MOD;

3.初始化 ==>为了填表的时候不越界

  • 因为当前位置依赖的是前3个位置,所以要将dp[1] = 1,dp[2] = 2,dp[3] = 4初始化,从第四个位置开始填表
  • 这里的dp[0]位置不需要处理,当然也可以处理dp[0] = 0

4.填表顺序

  • 从左往右填表

5.返回值

  • 返回dp[n] :到达n位置(n层台阶)的时候的方法数
class Solution {
public:
    const int MOD = 1e9+7;
    int waysToStep(int n) {
        if(n == 1 || n == 2) return n;
        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]) % MOD ) + dp[i-3]) % MOD;
        }
        return  dp[n];
    }
};

使用最小花费爬楼梯

https://leetcode.cn/problems/min-cost-climbing-stairs/

image-20230606113140489

注意坑点:此处的[0,n-1]位置代表的都是楼层,n位置代表的才是楼顶!!!

分析

**方法1:**以i位置为结尾…

1.状态表示

  • dp[i]:到达i位置的时候的最小花费

2.状态转移方程:根据最近的⼀步,分情况讨论

  • 由于支付i位置的费用之后,可以往后走1步/2步

image-20230606143432616

  • case1:先到达i-1位置,然后支付i-1位置的花费cost[i-1],从i-1位置往后走1步来到i位置
    • 此时的花费为:到达i-1位置时候的最小花费 +支付i-1位置的花费 dp[i-1] + cost[i-1]
  • case2:先到达i-2位置,然后支付i-2位置的花费cost[i-2],从i-2位置往后走2步来到i位置
    • 此时的花费为:到达i-2位置时候的最小花费 +支付i-2位置的花费 dp[i-2] + cost[i-2]
  • 此时dp[i]的值为case1和case2的较小值 dp[i] = min(dp[i-1] + cost[i-1],dp[i-2]+cost[i-2])

3.初始化

  • 当前i位置依赖前两个位置的值,需要初始化第0个和第1个位置,防止填表的时候越界
  • 题目已经说明:可以选择从下标为 0 或下标为 1 的台阶开始爬楼梯,所以dp[0] = dp[1] = 0

4.填表顺序

  • 从左往右,从第2个位置往后填表

5.返回值

  • 返回dp[n]:到达n位置(楼顶)的时候的最小花费
class Solution {
public:
    int minCostClimbingStairs(vector<int>& cost) {
        //楼梯顶部:n位置
        //dp[i]:到达i位置时的最小花费
        int n = cost.size();
        vector<int> dp(n+1);
        //可以选择从下标为 0 或下标为 1 的台阶开始爬楼梯 ==> 0和1位置不需要花费
        dp[0] = dp[1] = 0;
        //到达i位置时的最小花费 = min(到达i-1位置时的最小花费 + 花i-1位置的钱跳1步,到达i-2位置时的最小花费 + 花i-2位置的钱跳2步)
        for(int i = 2;i<=n;i++)
            dp[i] = min(dp[i-1] + cost[i-1],dp[i-2] + cost[i-2]);
        return dp[n];
    }
};

方法2:以i位置为起点…

1.状态表示

  • dp[i]:以i位置为起点,到达楼顶的时候的最小花费

2.状态转移方程:根据最近的⼀步,分情况讨论:

  • case1:支付i位置的花费之后,往后走一步,从i+1位置为起点到达楼顶, 此时花费为:cost[i] + dp[i+1]
  • case2:支付i位置的花费之后,往后走一步,从i+2位置为起点到达楼顶 ,此时花费为:cost[i] + dp[i+2]
  • 由于要的是最小花费,所以: dp[i] = min(dp[i + 1], dp[i + 2]) + cost[i]

image-20230606143620728

3.初始化

  • 为了保证填表的时候不越界,需要初始化最后两个位置的值,n-1位置到达楼顶的最小花费就是花费当前i-1位置的值,然后往后走1步,n-2位置同理
  • dp[n-1] = cost[n-1] dp[n-2] = cost[n-2]

4.填表顺序

  • 当前i位置依赖的是后面的两个位置,所以从n-3位置,右往左遍历

5.返回值

  • 因为最初可以站在0位置/1位置,所以应该返回dp[0]和dp[1]的较小值
class Solution {
public:
    int minCostClimbingStairs(vector<int>& cost) {
        int n = cost.size();
        //dp[i]:以i位置为起点,到达楼顶的最小花费
        //case1:花费i位置的钱,往后走1步到达i+1位置,然后再到达楼顶
        //case2:花费i位置的钱,往后走2步到达i+2位置,然后再到达楼顶
        //dp[i] = min(dp[i+1],dp[i+2]) + cost[i]
        vector<int> dp(n);
        dp[n-1] = cost[n-1],dp[n-2] = cost[n-2];
        for(int i = n - 3;i>=0;i--)
            dp[i] = min(dp[i+1],dp[i+2]) + cost[i];
        //可以选择从下标为 0 或下标为 1 的台阶开始爬楼梯,选择最小花费
        return min(dp[0],dp[1]);
    }
};

解码方法

https://leetcode.cn/problems/decode-ways/

分析

1.状态表示

  • dp[i]:以i位置为结尾时,解码的方法数 (字符串 [ 0 , i ] [0,i] [0i]区间上的字符,有多少种解码方法)

2.状态转移方程 ==>根据最近的一步划分问题

  • case1:i位置的数单独解码成一个字母

    • 解码成功:i位置的字符在 [ 1 , 9 ] [1,9] [1,9]之间才可以单独解码,此时以i位置结尾时解码的方法数 等于 以i-1位置结尾时解码的方法数,就是: [ 0 , i − 1 ] [0,i-1] [0i1]区间上的所有解码结果,后面跟上i位置解码后的字⺟
    • 解码失败:当 i i i位置上的数0的时候,此时i位置上的数是不能单独解码的,此时 [ 0 , i ] [0,i] [0i]区间上不存在解码结果,此时前⾯做的努⼒就全部⽩费了,dp[i] = 0
  • case2:i位置和i-1位置的数结合,共同解码成一个字母

    • 解码成功:当结合的数在$[10, 26] 之间的时候,说明 i − 1 位置和 i 位置的字符可以共同解码,此时以 i 位置结尾时解码的方法数 ∗ ∗ 等于 ∗ ∗ 以 i − 2 位置结尾时解码的方法数,也就是: 之间的时候,说明i-1位置和i位置的字符可以共同解码,此时以i位置结尾时解码的方法数 **等于** 以i-2位置结尾时解码的方法数,也就是: 之间的时候,说明i1位置和i位置的字符可以共同解码,此时以i位置结尾时解码的方法数等于i2位置结尾时解码的方法数,也就是:[0,i-2]$区间上的所有解码结果,后面跟i和i-1位置的共同解码后的字母
    • 解码失败:当结合的数在$[0, 9] $ || [ 27 , 99 ] [27,99] [27,99]的时候,说明 i i i i − 1 i-1 i1位置结合后解码失败,要注意有前导0的情况!!!如:00,01… ,此时 [ 0 , i ] [0,i] [0i]区间上不存在解码结果,此时前⾯做的努⼒就全部⽩费了,dp[i] = 0

假设i位置的字符为a,i-1位置的字符为b

image-20230606152441437

总结:

  • s [ i ] s[i] s[i] 上的数在 $[1, 9] $区间上时: dp[i] += dp[i - 1] ;
  • 当$ s[i - 1] $ 与$ s[i] $ 上的数结合后,在$ [10, 26] $ 之间的时候: dp[i] +=dp[i - 2]
  • 若上述两个判断都不成⽴,说明没有解码⽅法,dp[i] = 0

3.初始化

  • 需要初始化dp表0位置和1位置的值

  • 对于dp[0]: 如果0位置的字符是 ′ 0 ′ '0' 0 ,此时没有解码方法,dp[0] = 0。 如果不是字符0,那么可以解码成功,dp[0] = 1

  • 对于dp[1]:

    • 如果1位置的字符在 [ 1 , 9 ] [1,9] [1,9]之间,那么能单独解码,此时dp[1] = 1 ,否则dp[1] = 0。 错误!!! 必须要0位置和1位置都能单独解码,此时才算1种解码方法,例如: 06,此时1位置是能单独解码的,但是由于0位置不能单独解码!所以1位置单独解码是失败的

    • 如果0位置和1位置的字符结合后的数在 [ 10 , 26 ] [10,26] [1026]之间,说明前两个字符可以构成一种解码方法 dp[1] += 1

4.填表顺序

  • 从第二个位置开始,左往右填表

5.返回值

  • 返回dp[n-1]:以n-1位置为结尾时,解码的方法数
class Solution {
public:
    int numDecodings(string s) {
        int n = s.size();
        //dp[i]:以i位置为结尾(字符串[0,i]区间上的字符),有多少种解码方法数
        //case1:i位置单独解码 若成功:dp[i] += dp[i-1]
        //case2:i和i-1位置共同解码,若成功:dp[i] += dp[i-2]
        vector<int> dp(n);
        dp[0] = s[0] != '0'; //如果0位置是字符0,那么0位置就不能单独
        if(n == 1) return dp[0];

        //0位置和1位置都能单独解码,此时才算1种解码方法
        dp[1] = s[1] != '0' && s[0] != '0'; 
        int sum = (s[0] - '0') * 10 + (s[1] - '0');//0位置和1位置共同解码形成的值,如果范围在[10,26],才可以一起解码
        if(sum >= 10 && sum <= 26) dp[1] += 1;//此时0位置和1位置可以一起解码

        for(int i = 2;i<n;i++)
        {
            //i位置:单独解码 or 和i-1位置一起解码

            //case1:i位置单独解码,此时以i位置为结尾的解码方法数就是i-1位置结尾的解码方法数(也就是:[0,i-1]区间上的所有解码结果,后面跟上i位置解码后的字⺟)
            if(s[i] != '0') dp[i] += dp[i-1];
            //case2:i和i-1位置共同解码
            int sum = (s[i-1] - '0') * 10 + (s[i] - '0');
            //此时i位置和i-1位置可以一起解码,此时以i位置为结尾的解码方法数就是i-2位置结尾的解码方法数(也就是:[0,i-2]区间上的所有解码结果,后面跟上i和i-1位置解码后的字⺟)
            if(sum >= 10 && sum <= 26)
                dp[i] += dp[i-2];
        }
        return dp[n-1];
    }
};

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

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

相关文章

React 通过一个输入内容加入列表案例熟悉 Hook 基本使用

我们创建一个react项目 在src下创建components文件夹 在下面创建一个index.jsx index.jsx 参考代码如下 import React, { useState } from "react";const useInputValue (initialValue) > {const [value,setValue] useState(initialValue);return {value,onCha…

【2023,学点儿新Java-01】从查看本机 jdk版本 开始 | Java基础全程脉络图、Java工程师全程技术路线、Java职业晋升路线图

一个人把生命耗尽&#xff0c;应该是为了一些美好的东西&#xff0c;值得的东西&#xff0c;用蓬勃如烈火的生命力 去战胜一个又一个人生的悲剧&#xff0c;这本身就是人生的意义之一吧&#xff01; &#x1f3af;作者主页&#xff1a; 追光者♂&#x1f525; &#x1f338;个人…

Win10 系统专业版远程桌面如何才能多用户同时登录使用?

环境&#xff1a; Win10专业版19041 RDPWrap-v1.6.2 dell5493笔记本 问题描述&#xff1a; Win10 系统专业版远程桌面如何才能多用户同时登录使用&#xff1f; 解决方案&#xff1a; 安装RDPWrap 1.关闭remote desktop services服务 安装RDP之前&#xff0c;要先关闭re…

8年性能测试工程师告诉你,压力测试详解,如何做压力测试...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 什么是压力测试&a…

灯塔工厂PLC智慧边缘计算机嵌入式linux系统

随着工业物联网的发展&#xff0c;越来越多的企业开始将工业自动化设备接入到物联网平台&#xff0c;以实现设备远程监控、运维和管理。其中&#xff0c;PLC智能网关作为连接PLC和物联网平台的重要设备&#xff0c;发挥着至关重要的作用。本文将介绍PLC数据采集控制网关的功能和…

文心一言 VS 讯飞星火 VS chatgpt (42)-- 算法导论5.4 6题

六、假设将n个球投人 n 个箱子里&#xff0c;其中每次投球独立&#xff0c;并且每个球等可能落入任何箱子。空箱子的数目期望是多少?正好有一个球的箱子的数目期望是多少? 文心一言&#xff1a; 这是一个典型的概率问题。首先&#xff0c;我们可以通过简单的计算来理解问题…

【论文】YOLOv7运行train.py和test.py时,打印不出来计算量GFLOPs解决方法

目录 前提案例解决步骤运行方式打印效果 前提 我在AutoDL云服务器上&#xff0c;会运行失败&#xff0c;不晓得为啥&#xff0c;我感觉可能是CUDA版本的问题&#xff0c;CUDA版本低了就会没有那么深的计算量吧 AutoDL上&#xff08;显卡RTX3090&#xff0c;4GB&#xff09;安…

java的网络编程

一、网络编程的三要素 IP、端口号、协议。 特殊IP地址127.0.0.1&#xff0c;也可以是localhost&#xff0c;是回送地址也称回环地址&#xff0c;也称本机IP&#xff0c;永远只会寻找当前所在本机。 常见cmd命令&#xff1a; ipconfig:查看本机IP地址&#xff1b; ping:检查网络…

Innovus: dbGet 快速学习教程

dbGet是innovus/encounter工具自带的"database access command"命令中的一部分&#xff0c;它几乎可以用来获取设计相关的一切信息。 输入dbGet 按[Tab]键&#xff0c;能看到三个选项&#xff0c;分别是head / top /selected。这三个选项所代表的意义如下: head --…

ubuntu 22.04安装mysql 8.0与避坑指南

MySQL 是一个开源数据库管理系统&#xff0c;可作为流行的 LAMP&#xff08;Linux、Apache、MySQL、PHP/Python/Perl&#xff09;堆栈的一部分安装。 它实现了关系模型并使用结构化查询语言&#xff08; SQL&#xff09;来管理其数据。 本教程将介绍如何在 Ubuntu 22.04 服务器…

appium+python在Android端的环境配置

一、安装配置JDK 一、安装环境 1、本机系统&#xff1a;Windows 10&#xff08;64位&#xff09; 2、JDK版本&#xff1a;1.8&#xff08;64位&#xff09; 二、下载安装 1、JDK和JRE简介 Java环境分JDK和JRE &#xff0c;JDK就是Java Development Kit。简单的说JDK是面向…

JMeter安装图文及入门教程,(附视频教程)

目录 一、JMeter介绍 二、下载配置安装 三、JMeter入门压测实例 总结&#xff1a; 一、JMeter介绍 JMeter是Apache组织开发的基于Java的压力测试工具。用于对软件做压力测试&#xff0c;它最初被设计用于Web应用测试&#xff0c;但后来扩展到其他测试领域。它可以用于测试静…

Go语言并发之context标准库

1、Go语言并发之context标准库 Go中的 goroutine 之间没有父与子的关系&#xff0c;也就没有所谓子进程退出后的通知机制&#xff0c;多个 goroutine 都是平行地 被调度&#xff0c;多个 goroutine 如何协作工作涉及通信、同步、通知和退出四个方面。 通信&#xff1a;chan 通…

ResNet

论文信息 论文名称&#xff1a;Deep Residual Learning for Image Recognition 论文地址&#xff1a;https://arxiv.org/pdf/1512.03385.pdf 发表期刊&#xff1a;CVPR 发表年份&#xff1a;2016 主要问题 在引言中作者提出了一个问题&#xff1a;训练一个更好的网络是否像堆…

这个网站,多希望你早点知道,越早越好!

这是一个有趣、神奇的个人博客网站。 这是一个马斯克经常上的网站&#xff0c;而且马斯克还在推特上关注了这个网站的账号。 网站地址&#xff1a;https://waitbutwhy.com/ 这个网站上的内容并不多&#xff0c;网站2013年创建的&#xff0c;至今已有10年&#xff0c;一共才产出…

python爬虫工程师,如何从零开始部署Scrapyd+Feapder+Gerapy?

突然被告知要连着上整整十一天的班&#xff0c;有一点点累&#xff0c;简单更新一下内容吧&#xff0c;水个积分 关注公众号&#xff1a;python技术训练营&#xff0c;精选优质文档&#xff0c;好玩的项目 内容&#xff1a; 1.面试专题几十个大厂面试题 2.入门基础教程 3.11模块…

活动邀请函五秒钟下载即用

在日常中&#xff0c;人们都是以纸质的邀请函发送给被邀请者&#xff0c;不仅需要花费大量的精力和时间去书写发送活动邀请函&#xff0c;还存在着被邀请人没有及时收到活动邀请函而错过参与的时间等。而这样只需制作一份就可以全网分享&#xff0c;用户短时间就能收到活动邀请…

可变参数列表

"多少人都&#xff0c;生来纯洁完美&#xff0c;心底从不染漆黑。" 我们想要实现一个函数&#xff0c;这个函数的功能是返回一个整形的最大值。 emm&#xff0c;似乎有那点味道。但这应用场景似乎很受限制&#xff0c;因为这个函数比较的有效区间&#xff0c;只能装下…

Pycharm远程开发之全局pip,激活远程虚拟环境pip,以及pip的--user选项

前言 最近需要部署一下生成对抗网络的开发环境&#xff0c;我自己的笔记本没有带显卡&#xff0c;想到实验室的服务器有带显卡索性就用实验室服务器的环境开发&#xff0c;通过pycharm的远程开发功能连接到服务器&#xff0c;本来以为轻轻松松就可以开始写代码了&#xff0c;结…

springcloud整合nacos

1.订单服务&#xff08;order&#xff09; 1.1 安装nocas Nacos 快速开始 --注意&#xff1a;nacos 我的是 nacos-server-1.4.1.tar.gz 1.2 新建order-nacos 模块 1.3 修改pom文件 添加 nacos 依赖 1.4 配置文件添加 nacos 地址 1.5 创建启动类 使用 RestTemplate 方式调用服…