DP动态规划(下)

文章目录

    • 如何在动态规划中有效地使用`std::unordered_map`?
      • 以下是使用`std::unordered_map`进行动态规划的一些关键点:
        • 选择合适的键:
        • 考虑哈希冲突:
        • 优化哈希函数:
      • 下面是一个使用`std::unordered_map`解决斐波那契数列问题的例子:
    • -迷你结-
    • 如何处理动态规划中的边界条件?
      • 明确问题的起始状态:
        • 定义状态和状态转移方程:
        • 设置基础情况:
        • 防止数组越界:
        • 特殊状态处理:
        • 文档化边界条件:
    • -迷你结-
    • C++中的动态规划解决方案是否适用于其他编程语言?
        • 定义状态:
        • 初始化:
        • 状态转移方程:
        • 遍历顺序:
        • 记忆化:
    • 迷你结
    • 如何优化动态规划的内存使用以避免空间复杂度过高?
      • 空间优化技术:
        • 滚动数组/一维数组:
        • 空间换时间:
        • 原地修改:
        • 递归优化:
        • 迭代而非递归:
        • 压缩状态表示:
        • 使用哈希表代替数组:
        • 滑动窗口技术:
    • 迷你结
  • 大结
  • -结-
  • -DP动态规划-

文前声明:
由于Java我并不熟悉,大部分资料来源于网络,不正确的地方请在评论区留言告诉我!
DP这一块会比较难,篇幅较长,请耐心看完
喜欢的话请按小红心,您的支持是我最大的动力!

动态规划(Dynamic
Programming,简称DP)是一种在数学、计算机科学和经济学中使用的,通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法。这种方法通常用来解决具有重叠子问题和最优子结构特性的问题,以减少重复计算并找到全局最优解。

————————————————
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:上一篇/这一篇

如何在动态规划中有效地使用std::unordered_map?

在动态规划问题中,std::unordered_map可以被用作备忘录(memoization)技术的一部分,以存储中间计算结果并避免重复计算。相比于std::mapstd::unordered_map提供了平均时间复杂度为 O ( 1 ) O(1) O(1)的查找、插入和删除操作,这在动态规划中尤其有用,因为通常需要频繁地查询和更新状态。

以下是使用std::unordered_map进行动态规划的一些关键点:

选择合适的键:

• 键应该能唯一标识一个状态。
• 如果状态由多个变量决定,可以将它们组合成一个复合键,例如使用元组(在C++17及以上版本中使用std::tuplestd::pair)或者将变量编码为一个长整型(例如,使用位运算)。
• 初始化
std::unordered_map

  • 使用默认构造函数创建std::unordered_map实例。>>
  • 可以指定哈希函数和键的比较器(如果键类型不是基本类型,且标准哈希和比较不适用的话)。
  • 使用find[]操作符访问状态:

使用find方法检查键是否存在,如果不存在则返回end()迭代器,可以结合insert来插入新值。
使用[]操作符可以直接获取或插入键值对,但要注意它会自动插入键,即使键之前不存在。

考虑哈希冲突:

虽然std::unordered_map的平均查找时间是 O ( 1 ) O(1) O(1),但在最坏的情况下,如果哈希函数分配不当,性能可能会退化至 O ( n ) O(n) O(n)

优化哈希函数:

如果键的类型允许,编写一个更高效的自定义哈希函数可以提高性能。
对于复合键,确保所有组成部分都被考虑在内,并且哈希函数均匀分布以减少冲突。

下面是一个使用std::unordered_map解决斐波那契数列问题的例子:

#include <unordered_map>long long fib(int n, std::unordered_map<int, long long>& memo) {if (n <= 1)return n;// Check if value is already computedauto it = memo.find(n);if (it != memo.end())return it->second;// Compute and store the valuelong long result = fib(n - 1, memo) + fib(n - 2, memo);memo[n] = result;return result;
}int main() {std::unordered_map<int, long long> memo;int n = 50; // Calculate the 50th Fibonacci numberstd::cout << "Fibonacci(" << n << ") = " << fib(n, memo) << std::endl;return 0;
}

-迷你结-

在这个例子中,memo是一个std::unordered_map,用于存储已计算的斐波那契数。每次计算新的斐波那契数时,都会先检查memo中是否已有结果,如果没有,则计算后将其存入memo。这样就避免了重复计算相同的子问题,显著提高了效率.

如何处理动态规划中的边界条件?

处理动态规划问题时,正确设定边界条件是至关重要的,因为它们直接影响到递推关系式的建立及最终解的准确性。以下是一些处理边界条件的基本步骤和建议:

明确问题的起始状态:

首先,识别动态规划问题的初始状态。这是递推过程开始的地方,通常对应着问题规模最小的情况。例如,在计算斐波那契数列时,F(0)=0F(1)=1󠁪就是边界条件。

定义状态和状态转移方程:
  • 确定状态变量及其含义,然后根据问题逻辑构建状态转移方程。边界条件通常用于直接给出或推导出最基本状态的值。
设置基础情况:
  • 明确哪些是不需要进一步分解的最基本情形,并直接给出它们的解。这些基础情况通常是递归终止条件,例如,最短路径问题中起点到自身的距离为0。
防止数组越界:
  • 在实现动态规划时,确保数组或数据结构的索引不会超出边界。根据状态定义合理初始化数组大小,并在循环中正确控制索引。
特殊状态处理:
  • 对于某些特定输入或特殊情况,可能需要额外的边界条件处理。例如,在某些序列问题中,空序列或单元素序列可能需要特别考虑。
  • 验证边界条件的合理性:
  • 在实现后,通过测试用例特别是边界案例来验证边界条件的正确性,确保没有遗漏或错误。
文档化边界条件:
  • 在代码中清晰注释边界条件,有助于他人理解和维护代码。
    举例说明,在背包问题中,边界条件可能包括背包容量为0或没有物品可选时,最大价值为0;而在最短路径问题中,从源节点到自身的距离为0,这些都是直接给出解的边界情况。

-迷你结-

综上所述,边界条件的设定需要根据问题的具体情况仔细考虑,确保递推过程能够正确启动,并最终导向问题的解答。

C++中的动态规划解决方案是否适用于其他编程语言?

动态规划的核心思想和解决方案策略是跨语言的,这意味着在C++中实现的动态规划解决方案的基本原理和算法逻辑同样适用于其他编程语言,如Java、Python、JavaScript等。动态规划主要依赖于以下几个关键点:

定义状态:

明确表示问题状态的方式,这通常通过变量或数据结构来实现。

初始化:

确定初始状态的值,通常是问题的最简单形式或基本情况。

状态转移方程:

表达状态之间如何通过计算相互转换的公式或逻辑。

遍历顺序:

决定如何遍历状态空间,通常采用循环或递归来实现。

记忆化:

使用数据结构(如数组、哈希表等)来存储中间结果,避免重复计算。
不同编程语言在实现这些步骤时的语法和库支持会有所不同,但算法的核心逻辑保持一致。例如,C++中可能使用数组或std::vector来存储状态,而在Python中可能倾向于使用列表(list)或字典(dict)。Java开发者可能使用数组、ArrayListHashMap等。
下面是相同问题(最长公共子序列,LCS)在Python中的实现示例,以展示动态规划解决方案的跨语言适应性:

def longest_common_subsequence(text1, text2):m, n = len(text1), len(text2)dp = [[0] * (n + 1) for _ in range(m + 1)]for i in range(1, m + 1):for j in range(1, n + 1):if text1[i - 1] == text2[j - 1]:dp[i][j] = dp[i - 1][j - 1] + 1else:dp[i][j] = max(dp[i - 1][j], dp[i][j - 1])return dp[m][n]text1 = "ABCBDAB"
text2 = "BDCAB"
print("Longest Common Subsequence length:", longest_common_subsequence(text1, text2))

迷你结

这段Python代码实现了与上述C++示例相同的功能,展示了动态规划算法在不同编程环境中的通用性和适应性。

如何优化动态规划的内存使用以避免空间复杂度过高?

优化动态规划的内存使用,尤其是减少空间复杂度,是提高算法效率的一个重要方面。以下是一些常用的技术:

空间优化技术:

滚动数组/一维数组:

在许多情况下,动态规划表格中只有一行(或一列)是活跃的,即当前行的计算仅依赖于前一行的结果。因此,可以使用一个一维数组替代二维数组,每次计算完一行后覆盖旧的数据。以最长公共子序列为例,原本的二维dp数组可以降为一维,每次更新时只保留当前行和上一行的信息。

空间换时间:

虽然通常我们寻求减少空间使用,但在某些情况下,预计算并存储更多的数据可以减少后续计算,从而提升整体效率。这需要权衡空间和时间复杂度。

原地修改:

对于一些问题,可以考虑直接在输入数据上进行操作(如果允许),或者使用输入数据的一部分作为临时存储,减少额外内存的分配。

递归优化:

如果使用递归实现动态规划,可以通过记忆化递归(自顶向下,带缓存)减少重复计算,同时注意及时清理不再需要的缓存项,以避免内存泄露。

迭代而非递归:
  • 递归可能导致大量的调用栈,消耗内存。改用迭代方法(自底向上)通常可以减少内存使用。
压缩状态表示:

对于某些问题,状态可以用更紧凑的形式表示。例如,在某些状态转移中,只有有限的状态值是有效的,可以使用位运算来存储和操作状态,从而大幅减少空间需求。

使用哈希表代替数组:

当状态数量巨大但实际使用的状态远少于理论可能的状态时,可以使用哈希表(如unordered_map󠁪dict)来存储必要的状态,从而减少内存占用。

滑动窗口技术:

在处理序列问题时,如果状态只与最近的几个元素有关,可以使用滑动窗口来维持这些元素,而不是存储整个序列的状态。

迷你结

通过应用以上技术,可以在保持算法正确性的前提下,有效降低动态规划问题的空间复杂度,提升算法的实际运行效率。

大结

动态规划(Dynamic Programming,简称DP)是一种在数学、管理科学、计算机科学以及经济学等领域广泛应用的求解最优化问题的方法。它的核心思想是通过把原问题分解为相对简单的子问题,并且存储子问题的解,从而避免了重复计算,高效地解决具有重叠子问题和最优子结构的问题。

一个完整的动态规划问题分析通常包括以下几个步骤:

  1. 确定问题具有最优子结构
    首先,需要确认问题是否具有最优子结构,即原问题的最优解可以通过子问题的最优解组合得出。这是动态规划适用的前提条件。
  2. 定义状态和状态转移方程
  • 定义状态:明确问题中需要做出决策的变量及其可能取值。例如,在背包问题中,状态可能是“前i个物品放入容量为j的背包中的最大价值”。
  • 状态转移方程:表达如何从已解决的子问题的解推导出更大问题的解。这一步是动态规划的核心,它描述了状态之间的依赖关系。例如,背包问题的状态转移方程可能是
    dp[i][j] = max(dp[i-1][j], dp[i-1][j-weight[i]] + value[i])
    ,表示考虑第i个物品时,要么不选该物品,要么选择该物品并更新背包剩余容量下的最大价值。
  1. 边界条件和初始化
    确定动态规划表的边界条件,即最小子问题的解。例如,在上述背包问题中,初始时dp数组的值可能根据背包容量和物品重量设定,如
    dp[0][j] = 0
    表示没有物品时背包的价值为0
    dp[i][0] = 0
    表示背包容量为0时无法放置任何物品。
  2. 选择合适的计算顺序
    决定是采用自顶向下(递归+记忆化搜索)还是自底向上(迭代)的方式来填充动态规划表。自顶向下方法从较大的问题开始,逐步分解;自底向上则从最小的子问题开始,逐步构建更大的解。
  3. 构建解决方案
    根据动态规划表的信息,逆向构造出问题的最终解。这一步可能需要回溯记录哪些子问题的选择导致了最终的最优解。
    实例分析:斐波那契数列
    以斐波那契数列为例,动态规划分析如下:
  • 问题:计算第n项斐波那契数F(n),其中F(n)=F(n-1)+F(n-2)F(0)=0, F(1)=1
  • 最优子结构:F(n)的值依赖于F(n-1)F(n-2),具有最优子结构。
  • 状态定义:dp[i]表示斐波那契数列的第i项。
  • 状态转移方程:dp[n] = dp[n-1] + dp[n-2]
  • 边界条件:dp[0]=0, dp[1]=1
  • 计算顺序:自底向上,从dp[0]dp[1]开始,依次计算至dp[n]
  • 解决方案:最终dp[n]即为所求的斐波那契数列的第n项。

-结-

动态规划通过这种方式,将复杂问题分解为一系列简单子问题,通过解决这些子问题并存储结果来避免重复计算,从而高效地找到原问题的最优解。

-DP动态规划-


啊哈哈终于干完啦!!!!
姐今天要好好吃一顿 !!啊哈哈哈!!

好吧正经点
肝了时长两周半的钱白巧终于把最重要的DP给肝出来了
诶?还在看吗?给给小心心下期给你看更好的文章!
(有不对的地方请在评论区留言,让我们一起进步!)

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

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

相关文章

测试 halcon算子 derivate_gauss 高斯一阶导数卷积

参上了 matlab fileexchange 有人上传了高斯 dx,dy一阶导卷积代码 卷积核的计算我修改成了核元素绝对值求做分母 归一化 和halcon的 derivate_gauss算子的计算结果对别如下 还是不知道怎么做到两者结果一致. 测试图像: 我的: halcon的: 获取两份图像的灰度值到数组并做对应位…

即时聊天系统

功能描述 该项目是一个前后端分离的即时聊天项目&#xff0c;前端采用vue2、后端使用springboot以mysql8.0作为数据库。 项目功能包含了单聊、群聊功能。在此基础上增加了对好友的功能操作&#xff0c;如备注设为通知、视频聊天、语音聊天、置顶、拉入黑名单、清空聊天记录等。…

【面试干货】Integer 和 int 的区别

【面试干货】Integer 和 int 的区别 1、基本类型与包装类型2、内存占用3、自动装箱与拆箱4、null 值5、常量池6、总结 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 在Java中&#xff0c;Integer 和 int 是两种不同类型的变量&#xff0c;…

leetcode LRU 缓存

leetcode: LRU 缓存 LRU 全称为 Least Recently Used&#xff0c;最近最少使用&#xff0c;常常用于缓存机制&#xff0c;比如 cpu 的 cache 缓存&#xff0c;使用了 LRU 算法。LRU 用于缓存机制时&#xff0c;关键的是当缓存满的时候有新数据需要加载到缓存的&#xff0c;这个…

自动化测试断言

自动化判断测试用例的执行的结果是否成功&#xff0c;是通过判断测试得到的实际结果与预期结果是否相等决定的。这个时候就用到了断言。 检查点分为两个&#xff0c;一个是页面级别的检查&#xff0c;包括网页的标题和网址&#xff0c;以及是否包含某个文字 另一个检查点是页…

CSS从入门到精通——动画:CSS3动画延迟和完成后状态的保持

目录 任务描述 相关知识 动画状态 动画完成时的状态 动画延迟 编程要求 任务描述 本关任务&#xff1a;用 CSS3 实现小车等待红绿灯的效果。效果图如下&#xff1a; 相关知识 为了完成本关任务&#xff0c;你需要掌握&#xff1a;1.动画状态&#xff0c;2.动画完成时的状…

复旦大学辅修金融学专业

本文记录一下笔者本科时期在复旦大学辅修金融学专业所上过的课程。先是列出所有的课程名称&#xff0c;然后在GPT-4o的作用下生成每门课的主要研究内容。 主修课程&#xff1a; 第一学期&#xff1a;微观经济学&#xff0c;金融市场学&#xff0c;会计学 第二学期&#xff1a;…

[Cloud Networking] SPDY 协议

文章目录 1. 背景2. SPDY 之前3. SPDY 项目目标4. SPDY 功能特点4.1 SPDY基本功能4.2 SPDY高级功能 1. 背景 TCP是通用的、可靠的传输协议&#xff0c;提供保证交付、重复抑制、按顺序交付、流量控制、拥塞避免和其他传输特性。 HTTP是提供基本请求/响应语义的应用层协议。 不…

Linux下的串口通信

串口通信 基础知识&#xff1a; 什么是串口&#xff1f; 串口全称串行通信接口&#xff0c;是一种常用于电子设备之间通信的异步&#xff0c;全双工接口&#xff0c;典型的串口通信只需要 3 根线&#xff0c;分别是地线 (GND)&#xff0c;发送线(TX)&#xff0c;接收线(RX)。如…

【react小项目】bmi-calculator

bmi-calculator 目录 bmi-calculator初始化项目01大致布局01代码 02完善样式02代码 03输入信息模块03代码 04 使用图表04代码 05详细记录信息渲染05代码 06 让数据变成响应式的06-1输入框的数据处理06-2图表&#xff0c;和记录信息的区域数据处理 07 删除功能&#xff0c;撤销功…

C语言——文件

A1.文件 文件一般是指存储在外部介质上数据的集合操作系统以文件为单位对数据进行管理的输入输出是数据传送的过程&#xff0c;数据如流水一样&#xff0c;输入输出流数据的组织形式和数据文件分为ASCII文件和二级制文件 把内存中的数据按其在内存中的存储形式原样输出磁盘上存…

C# OpenCvSharp Mat操作-创建Mat-构造函数

🌟 Mat类:图像与多维矩阵的魔法 ✨ Mat类是OpenCvSharp中用于表示图像和多维矩阵的核心类。它提供了多种构造函数来创建和初始化矩阵对象。下面我们逐一解释这些构造函数,并通过示例来说明它们的用法。📸 🚀 默认构造函数 Mat() 创建一个空的Mat对象。 Mat mat = …

基于C#开发web网页管理系统模板流程-主界面统计功能完善

点击返回目录-> 基于C#开发web网页管理系统模板流程-总集篇-CSDN博客 前言 紧接上篇->基于C#开发web网页管理系统模板流程-主界面管理员入库和出库功能完善_c#web程序设计-CSDN博客 统计功能是管理系统很常见的功能&#xff0c;例如仓库管理系统要统计某时间段的出入库以…

QT信号与槽/窗口组件优化/使用QT制作QQ登录界面

使用手动连接&#xff0c;将登录框中的取消按钮使用第二中连接方式&#xff0c;右击转到槽&#xff0c;在该槽函数中&#xff0c;调用关闭函数 将登录按钮使用qt4版本的连接到自定义的槽函数中&#xff0c;在槽函数中判断u界面上输入的账号是否为"admin"&#xff0c;…

Python量化交易学习——Part7:定制增强型中证红利策略

中证红利指数是一个反映A股市场高红利股票整体状况和走势的指数。它通过选取上海、深圳交易所中现金股息率高、分红比较稳定、具有一定规模及流动性的100只股票作为样本。这个指数的目的是提供一个全面且具有代表性的视角,以观察A股市场中高红利股票的表现。中证红利指数的样本…

永磁同步直线电机(PMLSM)控制与仿真3-永磁同步直线电机数学三环控制整定

文章目录 1、电流环参数整定2、速度环参数整定3、位置环参数整定 写在前面&#xff1a;原本为一篇文章写完了永磁同步直线电机数学模型介绍&#xff0c;永磁同步直线电机数学模型搭建&#xff0c;以及永磁同步直线电机三环参数整定及三环仿真模型搭建&#xff0c;但因为篇幅较长…

HTML前端

html 超文本标记语言 文本&#xff1a;文字字符 超文本&#xff1a;网页内容 标记&#xff1a;标签 标识 提供许多标签&#xff0c;不同标签功能不同&#xff0c;网页就是通过这些标签描述出来的&#xff0c;最终由浏览器解释运行我们看到的网页 <!-- html注释<!DO…

C++ 50 之 继承中的对象模型

继承中的对象模型 在C编译器的内部可以理解为结构体&#xff0c;子类是由父类成员叠加子类新成员而成&#xff1a; #include <iostream> #include <string> using namespace std;class Base03{ public:int m_a; protected:int m_b; private:int m_c; // 哪怕是…

lua对接GPT4实现对话

演示效果&#xff1a; 准备材料&#xff1a; 1、FastWeb网站开发服务&#xff1a;fwlua.com 2、一台服务器 该示例使用开源项目&#xff1a;fastweb 实现。 代码比较简单&#xff0c;主要是两部分&#xff0c;一个lua代码和一个html页面&#xff0c;用来用户发起请求和后台…

面向事件编程之观察者模式

前言 村里的老人常说&#xff1a;真男人就该懂得遵守“三不原则”——不主动、不拒绝、不负责。 一个复杂的软件系统&#xff0c;其中必然会存在各种各样的“对象”&#xff0c;如果在设计之初没有注意控制好耦合度&#xff0c;导致各个对象甚至是函数之间高度耦合&#xff0…