代码随想录Day 32|leetcode题目:501.斐波那契数、70.爬楼梯、746.使用最小花费爬楼梯

news/2024/9/17 7:43:42/ 标签: leetcode, 算法, 数据结构, c++, 动态规划

提示:DDU,供自己复习使用。欢迎大家前来讨论~

文章目录

  • 动态规划理论基础
  • 一、理论基础
  • 二、题目
    • 题目一: 509. 斐波那契数
    • 题目二:70. 爬楼梯
      • 解题思路:
    • 题目三: 746. 使用最小花费爬楼梯
      • 解题思路
  • 总结


动态规划理论基础

开始动态规划算法

一、理论基础

1.1 什么是动态规划

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

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

1.2 动态规划的解题步骤

对于动态规划问题,将拆解为如下五步曲,这五步都搞清楚了,才能说把动态规划真的掌握了!

  1. 确定dp数组(dp table)以及下标的含义
  2. 确定递推公式
  3. dp数组如何初始化
  4. 确定遍历顺序
  5. 举例推导dp数组

1.3 动态规划应该如何debug

找问题的最好方式就是把dp数组打印出来,看看究竟是不是按照自己思路推导的!

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

二、题目

题目一: 509. 斐波那契数

509. 斐波那契数

解题思路:

动态规划

动规五部曲:

这里我们要用一个一维dp数组来保存递归的结果

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

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

  1. 确定递推公式

  2. dp数组如何初始化

int n = 4;
for (int i = 1; i <= n; i++) {for (int j = i + 1; j <= n; j++) {cout << i << " " << j << endl;}
}
  1. 确定遍历顺序

从递归公式dp[i] = dp[i - 1] + dp[i - 2];中可以看出,dp[i]是依赖 dp[i - 1] 和 dp[i - 2],那么遍历的顺序一定是从前到后遍历的

  1. 举例推导dp数组

按照这个递推公式dp[i] = dp[i - 1] + dp[i - 2],我们来推导一下,当N为10的时候,dp数组应该是如下的数列:

0 1 1 2 3 5 8 13 21 34 55

如果代码写出来,发现结果不对,就把dp数组打印出来看看和我们推导的数列是不是一致的。

以上我们用动规的方法分析完了,C++代码如下

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)

我们只需要维护两个数值就可以了,不需要记录整个序列。

代码如下:

class Solution {
public:int fib(int N) {if (N <= 1) return N;int dp[2];dp[0] = 0;dp[1] = 1;for (int i = 2; i <= N; i++) {int sum = dp[0] + dp[1];dp[0] = dp[1];dp[1] = sum;}return dp[1];}
};
  • 时间复杂度:O(n)
  • 空间复杂度:O(1)

递归解法

  • 递归函数的返回值以及参数

在这里要定义两个全局变量,一个用来存放符合条件单一结果,一个用来存放符合条件结果的集合。

class Solution {
public:int fib(int N) {if (N < 2) return N;return fib(N - 1) + fib(N - 2);}
};
  • 时间复杂度:O(2^n)
  • 空间复杂度:O(n),算上了编程语言中实现递归的系统栈所占空间

题目二:70. 爬楼梯

70. 爬楼梯

解题思路:

本题如果没有接触过的话,会感觉比较难,多举几个例子,就可以发现其规律。

爬到第一层楼梯有一种方法,爬到二层楼梯有两种方法。

那么第一层楼梯再跨两步就到第三层 ,第二层楼梯再跨一步就到第三层。

所以到第三层楼梯的状态可以由第二层楼梯 和 到第一层楼梯状态推导出来,那么就可以想到动态规划了。

动规五部曲:

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

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

  1. 确定递推公式

dp[i] = dp[i - 1] + dp[i - 2]

  1. dp数组如何初始化

  2. 确定遍历顺序

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

  1. 举例推导dp数组

举例当n为5的时候,dp table(dp数组)应该是这样的

70.爬楼梯
// 版本一
class Solution {
public:int climbStairs(int n) {if (n <= 1) return n; // 因为下面直接对dp[2]操作了,防止空指针vector<int> dp(n + 1);dp[1] = 1;dp[2] = 2;for (int i = 3; i <= n; i++) { // 注意i是从3开始的dp[i] = dp[i - 1] + dp[i - 2];}return dp[n];}
};
  • 时间复杂度: O ( n ) O(n) O(n)
  • 空间复杂度: O ( n ) O(n) O(n)

优化一下空间复杂度,代码如下:

// 版本二
class Solution {
public:int climbStairs(int n) {if (n <= 1) return n;int dp[3];dp[1] = 1;dp[2] = 2;for (int i = 3; i <= n; i++) {int sum = dp[1] + dp[2];dp[1] = dp[2];dp[2] = sum;}return dp[2];}
};
  • 时间复杂度: O ( n ) O(n) O(n)
  • 空间复杂度: O ( 1 ) O(1) O(1)

题目三: 746. 使用最小花费爬楼梯

746. 使用最小花费爬楼梯

解题思路

  1. 定义dp数组

    • dp[i] 表示到达第 i 个台阶所需的最少体力。
  2. 建立递推关系

    • 每个台阶 i 可以从台阶 i-1i-2 跳上来。
    • dp[i] = min(dp[i - 1] + cost[i - 1], dp[i - 2] + cost[i - 2])
  3. 初始化dp数组

    • dp[0]dp[1] 都初始化为 0,因为到达第一个台阶不需要体力。
  4. 确定遍历顺序

    • 从第一个台阶开始,逐个计算每个台阶的 dp[i] 值。
  5. 举例说明

    • 通过一个具体的例子(如 cost = [1, 100, 1, 1, 1, 100, 1, 1, 100, 1]),展示 dp 数组如何逐步构建。

    具体的结果如下图所示:

img
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 - 1] + cost[i - 1], dp[i - 2] + cost[i - 2]);}return dp[cost.size()];}
};
  • 时间复杂度:O(n)
  • 空间复杂度:O(n)

还可以优化空间复杂度,因为dp[i]就是由前两位推出来的,那么也不用dp数组了,C++代码如下:

// 版本二
class Solution {
public:int minCostClimbingStairs(vector<int>& cost) {int dp0 = 0;int dp1 = 0;for (int i = 2; i <= cost.size(); i++) {int dpi = min(dp1 + cost[i - 1], dp0 + cost[i - 2]);dp0 = dp1; // 记录一下前两位dp1 = dpi;}return dp1;}
};
  • 时间复杂度:O(n)
  • 空间复杂度:O(1)

总结

  • 动态规划五部曲:动态规划数组和下标的定义,递归公式,动态数组的初始化,确定遍历顺序,推导数组。
  • 动态规划的一些技巧,能维护变量就不要维护数组。

http://www.ppmy.cn/news/1522663.html

相关文章

【OpenWrt(3)】内网搭建iperf3测速服务器

下载的iperf3 网站&#xff1a;https://iperf.fr/iperf-download.php Window地址&#xff1a;https://github.com/ar51an/iperf3-win-builds 安卓&#xff1a;https://gitee.com/hiyanyx/magic-i-perf 文章目录 下载的iperf3Windows 服务器启动安卓客户端启动参考 Windows 服务…

363_C++_配合360_负责读取和处理录像数据RecordReader类

其中的变量们: 读取器未启用 (!m_bEnReader) 已经有一个读取操作正在进行 (m_bPending) 读取器还未启动 (!m_bStarted) lastRealBytes:计算这帧数据实际需要的总字节数(包括未处理的部分和对齐的填充字节) mLastOffset:表示上次处理数据时的偏移位置 lastRemain 计算出…

电路基础笔记 --- 第一章

关于电路吸收及发出功率的判断&#xff1a; 其实关于这个问题根据实际电流和电压更好判断&#xff0c;我们根据参考电压及参考电流再结合各自数值画出对于元件来说的实际电流方向和电位高低&#xff0c;在实际电流方向通过元件时如果电位变高则代表元件在产生功率&#xff0c;…

【Linux】进程控制(一)

1. 进程创建 &#xff08;一&#xff09;认识fork函数 从已存在进程中创建一个新进程&#xff08;新进程为子进程&#xff0c;而原进程为父进程&#xff09; 进程调用fork&#xff0c;当控制转移到内核中的fork代码后&#xff0c;内核做&#xff1a; 分配新的内存块和内核数…

如何恢复回收站中已删除/清空的文件

回收站清空后如何恢复已删除的文件&#xff1f;是否可以恢复永久删除的文件&#xff1f;或者最糟糕的是&#xff0c;如果文件直接被删除怎么办&#xff1f;本文将向您展示清空回收站后恢复已删除数据的最佳方法。 回收站清空后如何恢复已删除的文件&#xff1f; “回收站清空后…

将AI与情境定位结合以确保品牌安全

你可能会看到一些广告&#xff0c;感觉它们跟你在线阅读或观看的内容有奇怪的关联。这就是上下文广告在起作用。这种基于广告的解决方案在不断变化的数字环境中逐步发展&#xff0c;已经成为每个广告主的必备工具。不过&#xff0c;这种广告不只是把广告和上下文进行匹配这么简…

学习记录:js算法(二十八):删除排序链表中的重复元素、删除排序链表中的重复元素II

文章目录 删除排序链表中的重复元素我的思路解法一&#xff1a;循环解法二&#xff1a;递归 网上思路 删除排序链表中的重复元素 II我的思路网上思路 总结 删除排序链表中的重复元素 给定一个已排序的链表的头 head &#xff0c; 删除所有重复的元素&#xff0c;使每个元素只出…

在js渲染的dom中的事件中传递对象

在某些情况下&#xff0c;可能需要将整个对象或部分对象嵌入到 HTML 元素的属性中&#xff0c;可以将对象数据序列化为 JSON 字符串&#xff0c;存储在 data-* 自定义属性中。这样可以在事件中取出并解析对象数据&#xff1a; <!DOCTYPE html> <html lang"en&qu…

Docker | Win10 安装

环境准备 1. 开启 WSL 环境配置 Docker 在 Windows 中&#xff0c;可以依赖于两种环境&#xff0c;分别是&#xff1a;Hyper-V、WSL。 Hyper-V&#xff1a;是一个虚拟环境&#xff0c;也就是虚拟机。WSL&#xff1a;是 Windows 的 Linux 子系统(系统要求不低于 Window10 的 …

八,SpringBoot Web 开发访问静态资源(附+详细源码剖析)

八&#xff0c;SpringBoot Web 开发访问静态资源(附详细源码剖析) 文章目录 八&#xff0c;SpringBoot Web 开发访问静态资源(附详细源码剖析)1. 基本介绍2. 快速入门2.1 准备工作 3. 改变静态资源访问前缀&#xff0c;定义为我们自己想要的4. 改变Spring Boot当中的默认的静态…

为何iPhone 16系列的发布对苹果至关重要?

即将发布的iPhone 16系列对苹果来说将是至关重要的时刻&#xff0c;特别是在快速发展的AI智能手机市场背景下。随着Android制造商在集成先进AI功能方面领先一步&#xff0c;苹果正处于一个关键的转折点——赶上竞争对手不仅仅是选择&#xff0c;而是必须完成的任务。 AI竞赛&am…

【深度学习详解】Task3 实践方法论-分类任务实践 Datawhale X 李宏毅苹果书 AI夏令营

前言 综合之前的学习内容&#xff0c; 本篇将探究机器学习实践方法论 出现的问题及其原因 &#x1f34e; &#x1f34e; &#x1f34e; 系列文章导航 【深度学习详解】Task1 机器学习基础-线性模型 Datawhale X 李宏毅苹果书 AI夏令营 【深度学习详解】Task2 分段线性模型-引入…

正则表达式--python

正则表达式 1、简介 概述 正确的, 符合特定规则的 字符串. 英文名叫: Regular Expression, 简称叫: re, RegExp 作用 主要是校验数据 细节 学正则, 主要是学正则的规则. 即: 哪个符号表示什么含义. 关于正则, 要求很简单, 只要能用规则, 看懂别人写的式子, 且能简单修改即可,…

springboot、flowable 生成图片发布到Docker乱码问题

flowable自带的方法生成图片时&#xff0c;如设置字体为宋体&#xff0c;则本地测试没有问题&#xff0c;因为windows自带宋体字体库&#xff0c;但是如果发布到Docker&#xff0c;则会出现乱码问题&#xff0c;因为大部分Docker并不包含宋体字体库&#xff1b; 通过Java代码&a…

VitePress 路由重写:自定义目录结构与页面生成

在使用VitePress构建文档网站时&#xff0c;你可能会遇到项目结构复杂的情况&#xff0c;特别是当你的项目是一个包含多个包的monorepo时。为了更好地组织和管理文档&#xff0c;你可能希望将文档文件放置在源代码旁边。然而&#xff0c;VitePress默认会按照特定的目录结构生成…

springboot+mybatis+vue2分页功能开发

前端框架代码 <div class"block"><span class"demonstration">完整功能</span><el-paginationsize-change"handleSizeChange"current-change"handleCurrentChange":current-page"currentPage4":page-s…

Linux多线程——日志任务的线程池实现

文章目录 线程池日志系统完善线程池的实现线程数据线程池的实现完整代码 线程池 线程池可以说是把之前所有的内容全部串联起来的一个项目 我们这里实现一个简单的版本&#xff0c;可以对其进行扩展 线程池也是一种生产者消费者模型 生产者布置任务而消费者处理任务 主要运…

Android 存储之 SharedPreferences 编码模板(工具类编码)

一、SharedPreferences 1、基本介绍 SharedPreferences 是 Android 的一个轻量级存储工具&#xff0c;它采用 key - value 的键值对方式进行存储 它允许保存和读取应用中的基本数据类型&#xff0c;例如&#xff0c;String、int、float、boolean 等 保存共享参数键值对信息的…

html记账本改写:保存数据 localStorage。

<!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><title>记账本改写</title><style>table {user-select: none;/* width: 100%; */border-collapse: collapse;}table,th,td {border: 1px solid…

Hive服务部署及Datagrip工具使用

目录 Hive服务部署 Hiveserver2服务 1&#xff09;用户说明 2&#xff09;Hiveserver2部署 &#xff08;1&#xff09;Hadoop端配置 &#xff08;2&#xff09;Hive端配置 3&#xff09;测试 &#xff08;1&#xff09;启动Hiveserver2 &#xff08;2&#xff09;使用命…