代码随想录day32--动态规划理论基础

什么是动态规划

动态规划简称DP,如果某一问题有很多重叠子问题,使用动态规划是最有效的。

所以动态规划中每一个状态一定是由上一个状态推导出来的,这一点一定要和贪心区别出来,贪心没有状态推导,而是直接从局部直接选择最优。

在贪心中,有一个例子是背包问题。

eg:由N件物品和一个最多能背重量为W的背包。第i件物品的重量是weight[i],得到的价值是value[i]。每件物品只能使用一次,求解将哪些物品装进背包里物品价值总和最大。

动态规划中dp[j]是由dp[j-weight]推导出的,然后取max(dp[j],dp[j-weight[i]+value[i])。

但如果是使用贪心,每次拿物品只会选择一个最大的或者最小的,和之前的状态没有什么关系。所以贪心解决不了动态规划的问题。

大家只需要明白动态规划是由前一个状态推导出来的,而贪心是局部直接选出最优的。


动态规划的解题步骤

做动态规划的题目的时候,很多同学包括我自己都有一个误区,就算将状态转移公式背下,然后照葫芦画瓢,然后根据公式进行代码书写,无论题目是否通过,都不清楚我们使用的dp[i]到底有什么用。

状态转移公式(递推公式)是很重要,但是仔细了解过后,或发现,动态规划不仅仅由递推公式。

对于动态规划,将其拆分为以下五部曲,这五步都搞懂,就基本可以把动态规划掌握:

1.确定dp数组以及其下标的含义

2.确定递推公式

3.dp数组如何初始化

4.确定遍历顺序

5.举例推导dp数组

*一定不要忽略初始化,因为一些情况是递推公式决定了dp数组要如何初始化


动态规划应该如何debug

绝大部分同学,对于动态规划的题目是,看题解感觉会了,然后照葫芦画瓢,如果能过那就没关系,但是如果没过,那么怎么改都过不去,对于之前说的dp数组的初始化,递推公式,遍历顺序,都是处于一种什么都不明白的的状态。

找到问题的最好办法就是将dp数组打印出来,看看究竟是不是按照自己的思路推导的

做动态规划的题目,写代码之前一定要把状态转移在dp数组的上具体情况模拟一遍,心中有数,最后确定推出的是否是想要的结果。

然后再写代码,如果代码没有通过,那就打印dp数组,看看是不是自己预先推导的哪里有出入。

如果打印出和自己预先模拟推导的一样,那么就算自己的递归公式、初始化或者遍历顺序有问题。如何还是不一样,那就是代码实现的细节有问题了。

这才是一个完整的思考过程,而不是代码出现了问题,就毫无头绪的乱改,最后依旧过不了,或者是稀里糊涂的过

从现在开始,写动态规划的题目出现问题或者无法通过,自己可以先思考这三个问题:

1.这道题我距离推导状态公式了吗

2.我们打印dp数组了吗

3.打印出的dp数组和我想的一样吗

如果这三个问题实现了,那么基本上动态规划的题目也就是解决了

亦或者是可以更清晰的明白自己究竟是哪一点不明白,是状态转移不明白,还是具体代码不知道该怎么写,还是不理解遍历dp数组的顺序。这样带着目的性的问题,就很好了。


LeetCode509.斐波那契数

题目描述:

斐波那契数 (通常用 F(n) 表示)形成的序列称为 斐波那契数列 。该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和。也就是:

F(0) = 0,F(1) = 1
F(n) = F(n - 1) + F(n - 2),其中 n > 1

给定 n ,请计算 F(n) 。

示例 1:

输入:n = 2
输出:1
解释:F(2) = F(1) + F(0) = 1 + 0 = 1

示例 2:

输入:n = 3
输出:2
解释:F(3) = F(2) + F(1) = 1 + 1 = 2

示例 3:

输入:n = 4
输出:3
解释:F(4) = F(3) + F(2) = 2 + 1 = 3

解题思路:

·这道题大家再熟悉不过了,但是就是因为这题比较简单,所以大家可能并没有做什么分析,就顺手写过了,我们使用动态规划五部曲进行分析

1.确定dp数组以及下标的含义

dp[i]的定义为:第i个数的斐波那契数值是dp[i]

2.确定递推公式

因为题目已经把递推公式给我们了,所以可以直接使用 dp[i] = dp[i-1] + dp[i-2]

3.dp数组如何初始化

题目中给的也很明确了

dp[0] = 0;dp[1] = 1;

4.确定遍历顺序,从递归公式中我们可以看出dp[i]是依赖dp[i-1]和dp[i-2],那么遍历顺序,就算一定是从前到后遍历

5.举例推导dp数组

按照递推公式,我们推导一下,当n为10时,数列应该是:0 1 1 2 3 5 8 13 21 34 55

如果最终代码无法通过,我们就将dp数组打印出,观察与我们推导的数列是不是一致的

以上,使用动态规划的方法全部分析完毕,代码如下:

class Solution {
public:
    int fib(int n) {
        if(n <= 1) return n;
        vector<int> dp(n+1);
        dp[0] = 0;
        dp[1] = 1;
        for(int i = 2;i <= n;i++){
            dp[i] = dp[i-1]+dp[i-2];
        }
        return dp[n];
    }
};

·时间复杂度:O(n)

·空间复杂度:O(n)

总结:这道题虽然说非常的基础,但是我们严格按照动态规划五部曲进行分析,发现其实分析过程也并不会复杂,我们使用简单题目用于掌握方法,接下来的方法会越来越重要。

LeetCode70.爬楼梯

题目描述:

假设你正在爬楼梯。需要 n 阶你才能到达楼顶。

每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?

示例 1:

输入:n = 2
输出:2
解释:有两种方法可以爬到楼顶。
1. 1 阶 + 1 阶
2. 2 阶

示例 2:

输入:n = 3
输出:3
解释:有三种方法可以爬到楼顶。
1. 1 阶 + 1 阶 + 1 阶
2. 1 阶 + 2 阶
3. 2 阶 + 1 阶

解题思路:

·这题仔细分析,多举几个例子,就会发现其规律。爬一层楼梯有一种方法,爬两层楼梯有两种方法,爬三层楼梯就可以根据前两层的规律进行推导

我们来进行动态规划五部曲的分析:

1.确定dp数组以及下标的含义

dp[i]:爬到第i层楼梯,有dp[i]种方法

2.确定递推公式

这道题怎么样才能推导出dp[i]呢?

从dp[i]的定义可以看出,dp[i]可以有两个方向可以推导出

首先是dp[i-1],上i-1层楼梯,有dp[i-1]种方法,那么再一步跳一个台阶不就是dp[i]了

dp[i-2]也是一样的思想,有dp[i-2]种方法,那么再一步跳两个个台阶不就是dp[i]了

所以就可以得出结论 dp[i]就算dp[i-1]和dp[i-2]之和

在推导dp[i]的时候,一定要时刻想着dp[i]的定义,这就体现出确定dp数组以及下标的含义的重要性

3.dp数组如何初始化

dp[0] = 1,因为不动也是一种移动方法

dp[1] = 1,只有一步可以走

dp[2]  = 2 ,有一步和两步可以走,所以我们就可以从i=3开始递推

4.确定遍历顺序

从递推公式dp[i] = dp[i-1]+dp[i-2]可以看出,遍历顺序一定是从前向后的

5.举例推导dp数组

我们会发现这个数组推导出来就算斐波那契,如果代码出现问题,那么就把dp数组打印出来,看看是否和自己推导的一样

代码如下:

class Solution {
public:
    int climbStairs(int n) {
        if(n <= 1) return n;
        vector<int> dp(n+1);
        dp[0] = 1;
        dp[1] = 1;
        for(int i = 2;i <= n;i++){
            dp[i] = dp[i-1]+dp[i-2];
        }
        return dp[n];
    }
};

 时间复杂度:O(n)

空间复杂度:O(n) 

总结:这道题其实和斐波那契数题目是一样的,但是动态规划五部曲,却比上一题复杂了那么多

关键就算斐波那契题目描述中,已经把递归公式和如何初始化都给出来了,剩下的就很好推出

但是本题需要逐个分析,大家应该初步感受出了动态规划的五部曲了

LeetCode746.使用最小花费爬楼梯

题目描述:

给你一个整数数组 cost ,其中 cost[i] 是从楼梯第 i 个台阶向上爬需要支付的费用。一旦你支付此费用,即可选择向上爬一个或者两个台阶。

你可以选择从下标为 0 或下标为 1 的台阶开始爬楼梯。

请你计算并返回达到楼梯顶部的最低花费。

示例 1:

输入:cost = [10,15,20]
输出:15
解释:你将从下标为 1 的台阶开始。
- 支付 15 ,向上爬两个台阶,到达楼梯顶部。
总花费为 15 。

示例 2:

输入:cost = [1,100,1,1,1,100,1,1,100,1]
输出:6
解释:你将从下标为 0 的台阶开始。
- 支付 1 ,向上爬两个台阶,到达下标为 2 的台阶。
- 支付 1 ,向上爬两个台阶,到达下标为 4 的台阶。
- 支付 1 ,向上爬两个台阶,到达下标为 6 的台阶。
- 支付 1 ,向上爬一个台阶,到达下标为 7 的台阶。
- 支付 1 ,向上爬两个台阶,到达下标为 9 的台阶。
- 支付 1 ,向上爬一个台阶,到达楼梯顶部。
总花费为 6 。

解题思路:

1.确定dp数组以及下标的含义

使用动态规划,需要使用一个数组来记录状态,本题只需要一个一维数组dp[i]就可以了

dp[i]的定义:达到第i个台阶所花费的最少体力为dp[i]

2.对于递推公式

得到dp[i],需要知道dp[i-1]和dp[i-2]

而dp[i-1]跳到dp[i]需要花费dp[i-1]+cost[i-1]

dp[i-2]跳到dp[i]需要花费dp[i-2]+dp[i-2]

按照题目要求,所以我们选择最小的,dp[i] = min(dp[i-1]+cost[i-1],dp[i-2]+cost[i-2])

3.dp数组如何初始化

看了递推公式,所以我们就可以知道需要初始化dp[i-1]和dp[i-2],也就是dp[0]dp[1]

题目中说可以选择下标为0或下标为1的台阶开始爬楼梯,也就是说到达第0个台阶和第一个台阶是不花费的,所以dp[0] = dp[1] = 0

4.确定遍历顺序

因为是模拟台阶,而且dp[i]由dp[i-1]和dp[i-2]推出,所以是从前到后遍历cost数组即可\

5.举例推导dp数组

我们使用示例2进行举例

如果代码出现问题,就将dp数组打印出来,看看和如上推导是不是一样的

代码如下:

class Solution {
public:
    int minCostClimbingStairs(vector<int>& cost) {
        vector<int> dp(cost.size()+1);
        dp[0] = 0;
        dp[1] = 0;
        for(int i = 2;i <= cost.size();i++){
            dp[i] = min(dp[i-2]+cost[i-2],dp[i-1]+cost[i-1]);
        }
        return dp[cost.size()];
    }
};

·时间复杂度:O(n)

·空间复杂度:O(n)

总结:这题和爬楼梯也是类似,只是多了一个需要花费的部分,所以并没有什么难理解的地方

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【深度学习】主要提出者【Hinton】中国大会最新演讲【通往智能的两种道路】

「但我已经老了&#xff0c;我所希望的是像你们这样的年轻有为的研究人员&#xff0c;去想出我们如何能够拥有这些超级智能&#xff0c;使我们的生活变得更好&#xff0c;而不是被它们控制。」 6 月 10 日&#xff0c;在 2023 北京智源大会的闭幕式演讲中&#xff0c;在谈到如…

opengles 顶点坐标变换常用的矩阵(九)

文章目录 前言一、opengles 常用的模型矩阵1. 单位矩阵2. 缩放矩阵3. 位移矩阵4. 旋转矩阵二、第三方矩阵数学库1. glm1.1 ubuntu 上安装 glm 库1.2 glm 使用实例1.2.1 生成一个沿Y轴旋转45度的4x4旋转矩阵, 代码实例如下1.2.2 生成一个将物体移到到Z轴正方向坐标为5处的4x4 vi…

K线实战分析系列之七:行情顶部的看跌信号——黄昏星形态

K线实战分析系列之七&#xff1a;行情顶部的看跌信号——黄昏星形态 一、黄昏星形态二、黄昏线总结 一、黄昏星形态 二、黄昏线总结 黄昏星的高点形成阻力位&#xff0c;启明星的低点形成支撑位中间的星线实体与第一根K线的实体跳空区域比较宽&#xff0c;第三根K线覆盖了第一…

SSM项目集成Spring Security 4.X版本 之 加入DWZ,J-UI框架实现登录和主页菜单显示

目录 前言 一、加入DWZ J-UI框架 二、实现登录页面 三、实现主页面菜单显示 前言 大家好&#xff01;写文章之前先列出几篇相关文章。本文内容也在其项目中接续实现。 一. SSM项目集成Spring Security 4.X版本&#xff08;使用spring-security.xml 配置文件方式&#xff…

旅游组团自驾游拼团系统 微信小程序python+java+node.js+php

随着社会的发展&#xff0c;旅游业已成为全球经济中发展势头最强劲和规模最大的产业之一。为方便驴友出行&#xff0c;寻找旅游伙伴&#xff0c;更好的规划旅游计划&#xff0c;开发一款自驾游拼团小程序&#xff0c;通过微信小程序发起自驾游拼团&#xff0c;吸收有车或无车驴…

CentOS安装Redis教程

CentOS安装Redis教程 一、Redis安装包压缩文件下载二、把下载好的Redis安装包压缩文件上传到服务器三、Redis安装四、Redis相关配置五、运行redis并指定配置文件 一、Redis安装包压缩文件下载 下载地址&#xff1a;Redis官网 省事链接奉上&#xff1a;Redis 6.2.14网盘下载链接…

在Linux服务器上部署一个单机项目

目录 一、jdk安装 二、tomcat安装 三、MySQL安装 四、部署项目 一、jdk安装 1. 上传jdk安装包 jdk-8u151-linux-x64.tar.gz 进入opt目录&#xff0c;将安装包拖进去 2. 解压安装包 这里需要解压到usr/local目录下&#xff0c;在这里我新建一个文件夹保存解压后的文件 [r…

ElasticSearch语法

Elasticsearch 概念 入门学习: Index索引>MySQL 里的表(table)建表、增删改查(查询需要花费的学习时间最多)用客户端去调用 ElasticSearch(3 种)语法:SQL、代码的方法(4 种语法) ES 相比于 MySQL&#xff0c;能够自动帮我们做分词&#xff0c;能够非常高效、灵活地查询内…

5.2.鸿蒙LiteOS-M los_dispatch

目录 一、cortex-m4 los_dispatch.S代码分析坚持就有收获 一、cortex-m4 los_dispatch.S代码分析 .syntax unified #.syntax [unified | divided], 指定arm 汇编语法规则 .arch armv7e-m #指定平台, 与命令行参数-march同样的作用 .fpu fpv4-sp-d16 #指定浮点运算…

第九篇【传奇开心果系列】python文本和语音相互转换库技术点案例示例:SpeechRecognitio库开发会议记录和转录工具经典案例

传奇开心果博文系列 系列博文目录python文本和语音相互转换库技术点案例示例系列 博文目录前言一、雏形示例代码二、扩展思路介绍三、SpeechRecognition库多种语音识别引擎支持示例代码四、SpeechRecognition库实时语音转录示例代码五、SpeechRecognitio库转录文本中提取关键词…

GPT Pilot - 编写 95% 代码的开发工具!

在这篇博客介绍了GPT-pilot的研发细节&#xff0c;原作者将探讨GPT Pilot的技术内核 —— 一款基于GPT-4编写的开发工具&#xff0c;可以生成生产使用代码的应用。 你有没有想过&#xff0c;95%的应用代码&#xff0c;可以由AI编写&#xff0c;就像《钢铁侠》里的贾维斯一样&a…

DAY30--learning English

一、积累 1.budget 2.fabulous 3.strait 4.jut 5.grater 6.fillet 7.fin 8.decay 9.cartilage 10.gill 11.convex 12.concave 13.tender 14.trim 15.workload 16.knuckle 17.crevice 18.skew 19.membrane 20.delicate 二、练习 1.牛津原译 Budget /ˈbʌdʒɪt/ 1.[ CU]the…

一流的财务:搞数据!!!(干货)

“三流财务给数据&#xff0c;二流财务给分析报告&#xff0c;一流财务给....&#xff08;解决方案&#xff09;“这些文章应该很多人都看到过&#xff0c;这个口号粗看好像很有道理&#xff0c;但笔者并不认同&#xff0c;因为大家都忽略了一个重要的概念&#xff1a;数据&…

CrossOver2024国产版虚拟机软件有哪些功能呢?

除了办公应用场景&#xff0c;CrossOver虚拟机软件还可以在以下场景中使用&#xff1a; 设计与开发&#xff1a;对于设计师和开发人员来说&#xff0c;某些特定的Windows设计和开发工具可能在Mac或Linux上没有完美的替代品。此时&#xff0c;他们可以使用CrossOver来运行这些工…

SQL 中如何实现多表关联查询?

阅读本文之前请参阅----MySQL 数据库安装教程详解&#xff08;linux系统和windows系统&#xff09; 在SQL中&#xff0c;多表关联查询是通过使用JOIN操作来实现的&#xff0c;它允许你从两个或多个表中根据相关列的值来检索数据。以下是几种常见的JOIN类型&#xff1a; …

wpf 3d 后台加载模型和调整参数

下载了一个代码&#xff0c;加载obj模型&#xff1b;它的参数在xaml里&#xff0c;模型加载出来刚好&#xff1b; 然后加载另一个obj模型&#xff1b;加载出来之后大&#xff0c;偏到很高和左的位置&#xff1b; 它之前的摄像机位置&#xff0c; Position"9.94759830064…

Apache celeborn 安装及使用教程

1.下载安装包 https://celeborn.apache.org/download/ 测0.4.0时出现https://github.com/apache/incubator-celeborn/issues/835 2.解压 tar -xzvf apache-celeborn-0.3.2-incubating-bin.tgz 3.修改配置文件 cp celeborn-env.sh.template celeborn-env.shcp log4j2.xml.…

Canvas学习笔记02:canvas的路径扫盲,附代码案例

hello&#xff0c;我是贝格前端工场&#xff0c;最近在学习canvas&#xff0c;分享一些canvas的一些知识点笔记&#xff0c;本期分享canvas的路径知识&#xff0c;欢迎老铁们一同学习&#xff0c;欢迎关注&#xff0c;如有前端项目可以私信贝格。 一、什么是canvas路径 Canvas…

滑动窗口刷题(三)

1. 找到字符串中所有字母异位词 1.题目解析 比较易懂&#xff0c;不做解析。 2.算法思路 哈希表滑动窗口有效字符个数优化 创建两个哈希表&#xff0c;将p字符串存入哈希表2。 定义cnt存放有效字符个数。 进窗口&#xff1a;存入哈希表1&#xff0c;如果该元素在哈希1中的…

嵌入式中常见语言对内存管理基本方法

大家好&#xff0c;今天给大家分享一下&#xff0c;从语言角度来讲:对比常见的几种语言对内存的管理方法​。 (1&#xff09;汇编语言:根本没有任何内存管理&#xff0c;内存管理全靠程序员自己&#xff0c;汇编中操作内存时直接使用内存地址&#xff08;譬如0xd0020010 )&…