在开始对动态规划的讲解之前,我们需要先对记忆化搜索进行回顾:

什么是记忆化搜索?

在搜索过程中,当搜索树中存在大量重复的节点时,我们可以通过引入一个"备忘录"(通常是一个数组或哈希表)来优化计算。这个备忘录会记录第一次搜索到某个节点时的计算结果。当后续再次遇到相同的节点时,就可以直接从备忘录中获取之前存储的结果,避免了重复计算的开销。

例如,在计算斐波那契数列时,fib(5) = fib(4) + fib(3),而fib(4)又需要计算fib(3)+fib(2)。如果不使用记忆化,fib(3)会被重复计算多次。使用记忆化后,每个fib(n)只需计算一次。

递推改递归
在用记忆化搜索解决斐波那契数列问题时,如果我们观察备忘录的填写过程,会发现它是一个从左到右依次填充的过程。具体来说:

  1. 先计算并存储fib(0)和fib(1)的基础值
  2. 然后根据这两个值计算fib(2)
  3. 接着用fib(1)和fib(2)计算fib(3)
  4. 以此类推,每个新值都依赖于前面已计算好的值

这种自底向上的计算方式,实际上就是将递归过程改写成了循环形式的递推。这种改写不仅减少了函数调用的开销,还使得计算过程更加直观。

什么是动态规划?

动态规划(Dynamic Programming)是一种用于解决多阶段决策问题的算法思想。它将复杂问题分解为多个相互关联的子问题(称为状态),并通过保存子问题的解来避免重复计算,从而显著提高算法效率。

动态规划通常适用于具有以下特征的问题:

  1. 最优子结构:问题的最优解包含其子问题的最优解
  2. 重叠子问题:不同的决策序列会到达相同的子问题

从上述描述可以看出,动态规划结合了分治法的思想(将问题分解为子问题)和剪枝的思想(通过存储结果避免重复计算)。典型的应用场景包括:

  • 最短路径问题(如Floyd算法)
  • 背包问题
  • 最长公共子序列
  • 编辑距离计算

(在这篇文章中,笔者只讲解最基础的动态规划,典序应用场景的运用将留到后续更新中)

在递推形式的动态规划中,我们常用下面的专有名词来表述,因此,要学好并利用好动态规划,我们首先要弄清楚每一题中一下的概念:

  1. 状态表示:指 f 数组(备忘录)中,每一个格子所代表的含义。其中,这个数组也被称为dp数组,或者dp表。
  2. 状态转移方程:指 f 数组中,每一个格子是如何利用其他格子推导出来的。
  3. 初始化:在填表之前,根据题目中默认条件或问题的默认初始状态,将 f 数组中若干格子先填上值。
例题辅助理解:

光讲概念我相信很多小伙伴不能很好的理解或是看不懂,接下来,笔者将通过两个例题来帮助理解。

下楼梯:顽皮的小明发现,下楼梯时每步可以走 1 个台阶、2 个台阶或 3 个台阶。现在一共有 N 个台阶,你能帮小明算算有多少种方案吗?(洛谷P10250下楼梯)

在没有特殊限制的情况下,下台阶问题可以转化为上台阶问题来处理。具体来说,相当于小明每次可选择上1、2 或 3 级台阶。当我们要到达第 i 级台阶时,可能来自 i - 1、i - 2 或 i - 3 级台阶。基于这一思路,可以自然地推导出对应的状态转移方程。具体的算法流程如下所示:
算法原理:

  1. 状态表示:f[i] 表示走到第 i 级台阶有多少种方案
  2. 状态转移方程:f[i] = f[i - 1] + f[i - 2] + f[i - 3]
  3. 初始化:f[1] = 1 …根据题意自行填写
  4. 填表顺序:从小到大依次填写
  5. 最终结果:f[n]

代码实现:

int main()
{cin >> n;f[1] = 1, f[2] = 2, f[3] = 4;for (int i = 4; i <= n; i++)f[i] = f[i - 1] + f[i - 2] + f[i - 3];cout << f[n] << endl;return 0;
}

空间优化:通过采用滚动数组技术循环利用数组,可以有效降低空间复杂度。空间优化:通过采用滚动数组技术循环利用数组,可以有效降低空间复杂度。

int main()
{cin >> n;f[1] = 1, f[2] = 2, f[3] = 4;for (int i = 4; i <= n; i++)f[i % 4] = f[(i - 1) % 4] + f[(i - 2) % 4] + f[(i - 3) % 4];cout << f[n % 4] << endl;return 0;
}
数字三角形:观察下面的数字金字塔。写一个程序来查找从最高点到底部任意处结束的路径,使路径经过数字的和最大。每一步可以走到左下方的点也可以到达右下方的点(洛谷P1216数字三角形)。数字三角形

在题目中,要求我们找到一条最大路径,但在这个问题中,我们不能使用贪心算法(易证,这里不再赘述),此时我们就需要使用动态规划的方式解决,每一个位置 f[i][j] 都是由位置 f[i-1][j-1] 或 f[i-1][j] 移动到的,因此,我们只需要找到max(f[i - 1][j - 1], f[i - 1][j])再加上 a[i][j] 即可找到从起点到达 f[i][j] 位置的最大路径。最后,我们只需要遍历一下 f 数组的最下面一行即可找出最大路径。
算法原理:

  1. 状态表示:f[i][j]表示从[1,1]走到[i,j]位置时,所有方案下的最大权值
  2. 状态转移方程: f[i][j] = max(f[i - 1][j - 1], f[i - 1][j]) + a[i][j]
  3. 初始化:将所有格子全部初始化为负无穷
  4. 填表顺序:仅需保证从上到下即可
  5. 最终结果:最后一行的最大值

代码实现:

int main()
{cin >> n;memset(f, -0x3f, sizeof f);for (int i = 1; i <= n; i++)for (int j = 1; j <= i; j++)cin >> a[i][j];f[1][1] = a[1][1];for (int i = 2; i <= n; i++)for (int j = 1; j <= i; j++)f[i][j] = max(f[i - 1][j - 1], f[i - 1][j]) + a[i][j];int max = 0;for (int i = 1; i <= n; i++)if (f[n][i] > max)max = f[n][i];cout << max << endl;return 0;
}

空间优化:可以将 f 数组从二维降为一维。由于每次更新时都是从左到右顺序处理,且使用过的旧值不会被重复利用,因此可以不断覆盖原数组,仅保留一维数组来记录最终结果。可以将 f 数组从二维降为一维。由于每次更新时都是从左到右顺序处理,且使用过的旧值不会被重复利用,因此可以不断覆盖原数组,仅保留一维数组来记录最终结果。

int main()
{cin >> n;memset(f, -0x3f, sizeof f);for (int i = 1; i <= n; i++)for (int j = 1; j <= i; j++)cin >> a[i][j];f[1] = a[1][1];for (int i = 2; i <= n; i++)for (int j = 1; j <= i; j++)f[j] = max(f[j - 1], f[j]) + a[i][j];int max = 0;for (int i = 1; i <= n; i++)if (f[i] > max)max = f[i];cout << max << endl;return 0;
}

谢谢阅读!更新不易,点个赞再走吧

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

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Boost搜索引擎 网络库与前端(4)

文章目录前言一、引入网络库模块引入cpp-httplibcpp-httplib测试正式编写http_server二、前端模块三、项目的可能拓展总结前言 终于到了最后一篇喽&#xff0c;嘻嘻&#xff01; 一、引入网络库模块 引入cpp-httplib 下载地址如下&#xff0c;我个人不喜欢新版本   cpp-http…

Flink反压问题

背景在使用flink的过程中&#xff0c;多次遇到过反压&#xff08;backpressure&#xff09;的问题&#xff0c;这通常是因为数据处理的速率超过了数据源或下游系统的处理能力导致。反压的底层剖析网络流控一个重要的概念是网络流控&#xff0c;如上图&#xff0c;不同的Consume…

Day5-中间件与请求处理

昨天搞定了异步优化&#xff0c;今天来解决一些实际问题。Day4的API虽然性能不错&#xff0c;但还缺少一些企业级应用必备的功能。 现在的问题 前端无法访问API&#xff08;跨域问题&#xff09;没有请求日志&#xff0c;出问题难以排查错误信息格式不统一缺少统一的请求处理机…

【LeetCode热题100道笔记】反转链表

题目描述 给你单链表的头节点 head &#xff0c;请你反转链表&#xff0c;并返回反转后的链表。 示例 1&#xff1a;输入&#xff1a;head [1,2,3,4,5] 输出&#xff1a;[5,4,3,2,1] 示例 2&#xff1a;输入&#xff1a;head [1,2] 输出&#xff1a;[2,1] 示例 3&#xff1a;…

Oracle:select top 5

在Oracle数据库中实现SELECT TOP 5功能需采用特定语法&#xff0c;因其原生不支持TOP关键字。以下是两种主流实现方式&#xff1a;‌ROWNUM结合子查询‌先通过子查询排序数据&#xff0c;再在外层用ROWNUM限制行数&#xff1a;SELECT * FROM ( SELECT * FROM 表名 ORDER BY 排序…

Kubernetes(k8s) 增量更新 po

文章目录前言k8s 增量更新 po1. 导出要新建po 的控制器配置2. 配置详解3. 重新生效前言 如果您觉得有用的话&#xff0c;记得给博主点个赞&#xff0c;评论&#xff0c;收藏一键三连啊&#xff0c;写作不易啊^ _ ^。   而且听说点赞的人每天的运气都不会太差&#xff0c;实在…

基于stm32的车辆安全驾驶预警系统

若该文为原创文章&#xff0c;转载请注明原文出处。一、 项目背景与引言(一) 研究背景及意义道路交通安全是全球性的重大公共安全问题。据统计&#xff0c;绝大多数交通事故源于驾驶员的危险状态&#xff08;疲劳、分心、健康突发状况&#xff09;和危险驾驶行为&#xff08;超…

React学习教程,从入门到精通, React 新创建组件语法知识点及案例代码(11)

React 新创建组件语法知识点及案例代码 React 是由 Facebook 开发的一个用于构建用户界面的 JavaScript 库。随着 React 的不断发展&#xff0c;创建组件的方式也在不断演进。本文将详细介绍 React 中创建组件的最新语法&#xff0c;包括函数组件&#xff08;Functional Compo…

SQL Server全链路安全防护

SQL Server 的安全性是一个多层次、综合性的体系&#xff0c;旨在保护数据免受未授权访问、篡改和泄露。其核心安全机制可概括为以下几个方面&#xff1a;1. 身份验证&#xff08;Authentication&#xff09; Windows 身份验证&#xff1a; 使用 Windows 账户&#xff08;域/本…

如何利用Web3提升企业竞争力

在这个信息爆炸的时代&#xff0c;Web3技术以其独特的去中心化、透明性和用户主权特性&#xff0c;成为企业提升竞争力的新战场。本文将深入探讨企业如何把握Web3的浪潮&#xff0c;实现业务的飞跃。 1. 把握Web3的核心价值 Web3的核心在于去中心化、透明性和用户主权。这种模式…

HOW - 在浏览器下载一个 Excel 表格文件

文章目录一、技术方案二、前端具体实现代码分析转换逻辑注意事项一、技术方案 后台返回 base64 数据 {code: 0,data: "base64;...", }前端进行数据格式转化并下载成 Excel 文件 这篇文章主要介绍第二个步骤的实现。 二、前端具体实现 代码 src/utils/transform…

【Android】Room数据库的使用

三三要成为安卓糕手 引入 Room是一个抽象层&#xff0c;对SQLite进行了封装&#xff0c;简化了SQLite数据库的操作&#xff0c;让开发者能以更加对象化的方式进行数据库操作&#xff1b;Room解决了SQLite操作繁琐&#xff0c;容易产生错误的问题&#xff0c;让开发者能以更加对…

Next.js 介绍:为什么选择它来构建你的下一个 Web 应用?

Next.js 介绍&#xff1a;为什么选择它来构建你的下一个 Web 应用&#xff1f; 作者&#xff1a;码力无边你好&#xff0c;欢迎来到我们的 Next.js 专栏&#xff01;在接下来的 30 篇文章中&#xff0c;我们将一起踏上一段从入门到精通的旅程&#xff0c;深入探索这个强大而优雅…

开发环境 之 编辑器、编译器、IDE梳理

小生第一次学习编程时&#xff0c;懵懵搞不懂编辑器、编译器、IDE区别&#xff0c;虽然这对前期学习编程语言语法的影响不是很大&#xff0c;但是现在梳理一下&#xff0c;总归心里踏实些。 一、概念及区别 IDE是前面几者的集成&#xff0c;前面几个分别是IDE的子集。对比维度编…

高级RAG策略学习(六)——Contextual Chunk Headers(CCH)技术

Contextual Chunk Headers&#xff08;CCH&#xff09;技术深度解析 第一部分&#xff1a;理论基础与核心原理 一、核心定义&#xff1a;给 “文本块” 加 “上下文标签” Contextual Chunk Headers&#xff08;上下文块标题&#xff0c;简称 CCH&#xff09;本质是为文档拆分后…

人形机器人控制系统核心芯片从SoC到ASIC的进化路径

目录&#xff1a; 0 前言 1 人形机器人控制系统核心芯片选择ASIC而非SoC的理由 1.1 SoC的架构特征 1.2 ASIC的架构特征 1.3 SoC的优势&#xff08;继承软件生态&#xff09; 1.4 ASIC的优势&#xff08;硬件底层算法就是应用层算法&#xff09; 1.5 人形机器人控制系统核…

linux thread 线程一

thread线程是linux的重要概念。线程不能独立存在&#xff0c;必须在进程中存在。一个进程必须有一个线程&#xff0c;如果进程中没有创建新线程&#xff0c;进程启动后本身就有一个线程。使用getpid、getppid获取进程的进程ID和父进程ID。使用pthread_self获取到当前线程的ID。…

Arduino Nano33 BLESense Rev2【室内空气质量检测语音识别蓝牙调光台灯】

一、硬件介绍 1、产品特点 Arduino Nano 33 BLE Rev2&#xff0c;利用了nRF52840微控制器的先进功能。这款32位Arm Cortex-M4 CPU 64 MHz与MicroPython的兼容性增强了板子的灵活性&#xff0c;该开发板的突出特点是其蓝牙低功耗&#xff08;BLE&#xff09;功能&#xff0c;使…

【问题解决】mac笔记本遇到鼠标无法点击键盘可响应处理办法?(Command+Option+P+R)

背景 如题。鼠标无法点击&#xff0c;但可以移动。触控板能够波动&#xff0c;鼠标翻页能够work&#xff0c;但是点击后无法响应。 根因 电脑缓存问题 解决办法 重置PRAM&#xff1a; 确保电脑关机状态&#xff08;可以先sudo shutdown -t now)&#xff08;一定要确保&#xff…

23ai数据库通过SQLcl生成AWR报告

‌1. 查看现有快照SQL> awr list snap;SNAP_ID DBID BEGIN_INTERVAL_TIME END_INTERVAL_TIME FLUSH_LEVEL __________ _____________ __________________________________ __________________________________ ______________793 …