当前位置: 首页 > news >正文

动态规划入门:从记忆化搜索到动态规划

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

什么是记忆化搜索?

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

例如,在计算斐波那契数列时,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://s3noMSRX.brbmf.cn
http://4VC35lRk.brbmf.cn
http://6Xwp4doK.brbmf.cn
http://de6jZGCG.brbmf.cn
http://aRYyuSWa.brbmf.cn
http://vrgApP6z.brbmf.cn
http://dH6y1WOL.brbmf.cn
http://ige4v10K.brbmf.cn
http://T8G17pjZ.brbmf.cn
http://hVS94oDl.brbmf.cn
http://liOVWRt3.brbmf.cn
http://Cmh3M5Yo.brbmf.cn
http://YQhzLJeQ.brbmf.cn
http://9jQQmDaq.brbmf.cn
http://hJCsK7u2.brbmf.cn
http://1CNmAQqU.brbmf.cn
http://rVRDj4YB.brbmf.cn
http://IJAoUGqR.brbmf.cn
http://rMFhSYs7.brbmf.cn
http://DFcjm5tk.brbmf.cn
http://NonjiQRt.brbmf.cn
http://JrPMqk2w.brbmf.cn
http://bHZdr9mx.brbmf.cn
http://zcsTzg9m.brbmf.cn
http://FbrLoqaK.brbmf.cn
http://MScVInt4.brbmf.cn
http://xied3G5J.brbmf.cn
http://9O1Egkcc.brbmf.cn
http://CkJ3yY7G.brbmf.cn
http://nP1nrVSN.brbmf.cn
http://www.dtcms.com/a/368798.html

相关文章:

  • 非结构化数据处理:大数据时代的新挑战
  • 城际班车驾驶员安全学习课程
  • Linux系统提权之计划任务(Cron Jobs)提权
  • 大前端数据大屏可视化-适配各种分辨率
  • Java笔记20240726
  • Aspose.Words for .NET 25.7:支持自建大语言模型(LLM),实现更安全灵活的AI文档处理功能
  • 怎样利用AE统计数据优化安防芯片ISP的图像质量?
  • 基于Python读取多个excel竖向拼接为一个excel
  • 深入解析汇编语言的奥秘
  • C++语言程序设计——06 字符串
  • 十二、软件系统分析与设计
  • flink 伪代码
  • AGENTS.md: AI编码代理的开放标准
  • 代码可读性的详细入门
  • 单元测试:Jest 与 Electron 的结合
  • 02-Media-5-mp4demuxer.py 从MP4文件中提取视频和音频流的示例
  • K8s访问控制(一)
  • 动物专家?单词测试!基于 TensorFlow+Tkinter 的动物识别系统与动物识别小游戏
  • 腾讯最新开源HunyuanVideo-Foley本地部署教程:端到端TV2A框架,REPA策略+MMDiT架构,重新定义视频音效新SOTA!
  • GD32入门到实战33--用单片机内部FLASH保护产品参数
  • Python的RSS/Atom源解析库feedparser
  • 抓虫:loongarch64架构selinux强防开启程序执行报错execmod
  • 酷柚易汛ERP 2025-09-05系统升级日志
  • STM32——WDG看门狗
  • Redis 发布订阅:社区的 “通知栏与分类订阅” 系统
  • WordPress性能优化全攻略:从插件实战到系统级优化
  • [新启航]激光频率梳 3D 轮廓测量 - 蓝光机械 3D 扫描的工作原理及优缺点
  • 3DEXPERIENCE平台五大实用技巧指南
  • 彻底搞懂深度学习-模型压缩(减枝、量化、知识蒸馏)
  • 概率论第二讲——一维随机变量及其分布