【夜深人静学数据结构与算法 | 第十篇】动态规划

news2024/11/24 20:28:16

目录

前言:

动态规划:

常见应用:

解题步骤:

 动态规划的简化步骤:

案例:

509. 斐波那契数 - 力扣(LeetCode)

70. 爬楼梯 - 力扣(LeetCode)

62. 不同路径 - 力扣(LeetCode)

总结:


前言:

        本文我们将为大家讲解一下动态规划的理论知识,并且会讲解几道力扣的经典例题。各位如果感兴趣可以点击进来阅读。

动态规划:

动态规划是一种解决问题的数学思想和算法。它通常用于解决优化问题,即在一系列决策中找到最优解。动态规划的关键是将问题划分为子问题并进行递推求解。实际上,动态规划可以看作是将大问题划分为更小的,重复的子问题的解决方法,通过存储子问题的解并重复使用它们来减少计算。因此动态规划中经常会用到记忆化搜索状态压缩

        记忆化搜索是一种优化搜索的方法,通过对已经计算过的结果进行存储和重复使用,避免重复的计算。递归的过程中,对于每一个计算子问题的过程,将其结果存储下来,同时若下一次计算相同的子问题,则直接从存储中取出结果,避免了重复的计算过程。记忆化搜索算法常用于动态规划,可以提高算法的效率,避免运算时间复杂度的爆炸性增长。 

        状态压缩通常用于离散化的问题中,将问题中的某个状态按照一定的规则进行编码,转换成一个整数或者一个二进制数,以便在算法运算中更快速、简洁地处理这些状态。状态压缩通常用于位运算、哈希表,或者开数组等数据结构上,其优点是占用的空间小、时间复杂度低。在某些场景下,通过状态压缩,可以将时间复杂度降低到O(1)的级别。常见的状态压缩的应用包括华容道、八皇后、数独、迷宫等。

常见应用:

动态规划是一种思想和方法,可以应用于很多问题。以下是常见的动态规划应用:

1. 背包问题:将不同重量和价值的物品装入一个背包中,使得背包中的物品总价值最大。

2. 最长公共子序列问题:找出两个字符串中最长的公共子序列,即两个字符串中都有的最长子序列。

3. 最短路径问题:在有向图中找出从源节点到终节点的最短路径,通常使用迪杰斯特拉算法和贝尔曼-福德算法等。

        迪杰斯特拉算法贝尔曼-福德算法都是解决最短路径问题的算法,但它们的实现和应用领域略有不同。

        迪杰斯特拉算法是一种单源最短路径算法,用于解决只有一个起点的有向图或无向图中,从起点到其他所有节点的最短路径问题。迪杰斯特拉算法的基本思想是不断更新起点到图中其他各节点的距离,并选择最短路径进行扩展,直到所有节点已经更新为止,最终得到起点到其它所有节点的最短路径。由于迪杰斯特拉算法中有很多重复计算和不必要的比较,通常采用优先队列等数据结构优化算法性能。

        贝尔曼-福德算法是一种多源最短路径算法,可以解决具有负边权的有向图或无向图中的最短路径问题,但不支持负环图。贝尔曼-福德算法的基本思路是通过松弛操作,不断更新每条边连接的节点之间的最短路径长度。在算法迭代的过程中,对于每条边的更新,都要对所有边进行一遍松弛操作。直到更新次数达到最大允许的次数或不存在负环路为止,最终得到所有节点之间的最短路径。相比于迪杰斯特拉算法,贝尔曼-福德算法适用范围更广,但时间复杂度较高。

在图论中,松弛操作是指通过放宽约束条件来尝试更新节点之间的最短距离。松弛操作通常用于解决最短路径问题

总之,迪杰斯特拉算法和贝尔曼-福德算法都是解决最短路径问题的重要算法,可以根据具体问题的特点选择相应的算法加以运用。

4. 切割钢管问题:将一条长度为n的钢棒切割成若干段,每段长度为整数,求切割方案使得切割后的钢管价值最大。

5. 找零钱问题:给定不同面额的硬币,求将钱数为n的钱凑成指定的金额所需的最小硬币数。

6. 矩阵链乘法问题:在许多矩阵中把它们相乘,求完成乘法所需的最小数目的基本乘法操作次数。

7. 字符串编辑问题:计算将一个字符串转换成另一个字符串所需的最小操作(插入、删除、替换)次数。

8.股票买卖问题:如何操作自己的股票才能获得最大利益。

这就是动态规划的主要题型,我们实际上也只需要掌握这些题型就够了,足够应对大厂的面试以及各种动态规划竞赛题目。

解题步骤:

动态规划是一种求解最优化问题的算法思想,通常包括以下几个步骤:

1. 确定状态及其含义:根据问题的特点,确定状态及其含义,状态是动态规划算法的核心,从而建立与子问题之间的关系。

2. 确定状态转移方程:根据前一步确定的状态,找到各个阶段之间的联系,推导出状态转移方程,即当前状态与前一个状态之间的关系,这是求解最优解的关键。

3. 确定初始值:将问题转化为分阶段决策问题,并根据问题的实际情况确定初始状态值,准备迭代求解过程。

4. 迭代求解:根据状态转移方程,通过迭代计算,求解出所有子问题的最优解,进而得到问题的最优解。

5. 优化空间复杂度:在求解过程中,考虑压缩状态空间,减少存储需要从而达到优化空间复杂度的效果。这是优化算法的重要手段。

在实际应用中,以上步骤并不是一定需要全部按照顺序执行的,有时可以根据问题的情况先确定某些状态,再迭代求解,避免状态数量过多造成的时间和空间开销。

而动态规划的精髓就在于确定状态转移方程,以及利用状态转移方程确定DP数组。

在动态规划中,DP数组表示存储中间状态的一维或多维数组,也称为状态转移表或者状态转移矩阵。DP数组的作用是记录计算过程中的结果,以便于之后的计算。

在动态规划算法中,通常需要根据问题中定义的状态转移方程,将问题划分成一个个小问题,并计算出每个子问题的结果,将这些结果存储在DP数组中。之后,根据这些子问题的结果,通过状态转移方程计算出问题的最终结果。

DP数组的维度和含义通常由问题的性质和状态转移方程所决定。例如,对于背包问题,DP数组通常为二维数组,其中第一维表示物品个数,第二维表示背包容量,DP数组中的每个元素表示在前i个物品中,填满当前容量为j的背包时,所获得的最大价值。

需要注意的是,DP数组的存储空间通常比较大,特别是在处理大规模问题时。因此,在实际应用中,需要根据问题的实际情况,采取合理的优化措施,如状态压缩等,以减少存储空间的占用和算法的时间复杂度。

 动态规划的简化步骤:

1.DP数组的含义以及下标的含义。

2.递推公式的推导。

3.DP数组的初始化。

4.DP数组遍历遍历顺序(背包问题)

其实整个动态规划的类型题,都可以按照这五步来确定出最终代码。

我们接下来通过例题来带大家学习什么是动态规划。

案例:

509. 斐波那契数 - 力扣(LeetCode)

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

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

我们如果按照动态规划的思路来做:

1.DP数组的含义以及下标含义:此时 DP[i] 表示的是第i个数字的 斐波那契数 。

2.确定递推公式:这里题目已经直接给出F(n)=F(n-1)+F(n-2)

3.DP数组的初始化:题目已经给出F(0)=0,F(1)=1。

4.遍历顺序:因为我们要知道第n个数字的 斐波那契数,就要知道第(n-1)和第(n-2)个数字的 斐波那契数,因此我们这道题的遍历顺序采取从前往后进行遍历。

那我们已经知道了递推公式以及DP数组含义以及遍历,直接写代码就可以。

class Solution {
public:
    int fib(int n) {
          int nums[31];
          nums[0]=0;
          nums[1]=1;

          for(int i=2;i<=n;i++)
          {
              nums[i]=nums[i-1]+nums[i-2];
          }
          return nums[n];
    }
};

这道题属于动态规划的最基础入门题目,他向我们揭示了动态规划的最基础解题思路。

70. 爬楼梯 - 力扣(LeetCode)

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

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

 我们按照动态规划的思路来做:

1.DP数组的下标及其含义nums[i]  表示  到达第  阶台阶的方法数
2.确定递推公式:

网上太多只讲理论不讲为什么,我们在这里手推一下为什么第n阶台阶的走法是第(n-1)阶台阶的走法加上第(n-2)阶台阶的走法,(我们以第五阶台阶为例)

第一阶 :1

第二阶 :11    2

第三阶:111    12    21

此时要想直接到第五阶台阶需要走两步(直接在第三阶的方法上加2)

1112,122,212

第四阶:112   22  1111 122   211

此时想到第五阶台阶需要走一步(直接在第四阶的方法上加1)

 1121 221  11111  1221  21111

我们可以发现其实第五步全是第三阶和第四阶走法变种。

其实这道题唯一一个比较绕的是:很多同学不理解为什么我们到达num[i-1]或num[i-2]之后,不是还要再往前走一步或者两步嘛?为什么就可以不要这一两步呢?
其实就是因为我们这道题要的是方法,不是步数。而我们自己规定了只能走一步或者两步后,假设我们此时到达num[i-2]之后,其实方法就已经固定了,因为我们接下来只能往向前走两步(如果走一步那就与nums[i-1]重合了)。是不可能再产生新方法的,num[i-1]同理.

因此我们判断出递推公式为:nums[i]=nums[i-1]+nums[i-2]。

3.DP数组的初始化:num[1]=1.nums[2]=2。很通俗易懂的初始化。

4.遍历顺序:由本题的公式nums[i]=nums[i-1]+nums[i-2] 就可以知道这道题的遍历顺序需要是从前往后。

通过这个思路我们也可以快速写出本题代码。

class Solution {
public:
    int climbStairs(int n) {
        int nums[46];
        nums[0]=0;
        nums[1]=1;
        nums[2]=2;
        for(int i=3;i<=n;i++)
        {
            nums[i]=nums[i-1]+nums[i-2];
        }
        return nums[n];

    }
};

62. 不同路径 - 力扣(LeetCode)

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。

机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。

问总共有多少条不同的路径?

 这道题就属于进阶的动态规划题目这道题的最基本思路就是我们记录这个机器人从起点开始到每一个格子的不同路径条数,这样不断的推,就可以推出到达finish的不同路径数。

我们按照动态规划的思路来做:

1.DP数组的含义以及下标含义:nums[i][j]  表示机器人从  起点  开始到  nums[i][j]  的不同路径条数

2.确定递推公式:因为机器人只能是向下或者向右走,那么num[i][j]  =  nums[i-1][j]   +  nums[i][j-1].

3.DP数组的初始化:这个题初始化这里也是比较绕的:我们只能向下或者向右移动,那么我们就需要知道向下移动时本格的路径数以及向右移动时本格的路径数

也就是说标红色的格子一定要进行初始化,否则你想向左或者向右移动的时候无法利用递推公式。

而正因为它只能向下或向右移动,也就是说标红色的这些各种路径数都只有一条,也就是只能向右或者向下走

4.遍历顺序:那我们此时只需要挨个遍历这些未标红的格子不就好了。

那我们已经知道了递推公式以及DP数组含义以及遍历,直接写代码就可以。

class Solution {
public:
    int uniquePaths(int m, int n) {
      vector<vector<int>> nums(m, vector<int>(n, 0));

        for(int i=0;i<m;i++)
        {
            nums[i][0]=1; 
        }
        for(int j=0;j<n;j++)
        {
            nums[0][j]=1;
        }
        for(int i=1;i<m;i++) 
        {
            for(int j=1;j<n;j++)
            {
                nums[i][j]=nums[i-1][j]+nums[i][j-1]; 
            }
        }
        return nums[m-1][n-1];
    }
};

总结:

        关于动态规划的理论知识以及案例讲解我们就到这里,相应的刷题篇也会更新在leetcode合集里面,并且比较重要的背包问题我们会单独出一篇进行讲解的,各位同学如果感兴趣可以订阅该专栏,跟着我一起学透算法,让我们一起加油吧!

如果我的内容对你有帮助,请点赞,评论,收藏创作不易,大家的支持就是我坚持下去的动力!

 

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

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

相关文章

【软考网络管理员】2023年软考网管初级常见知识考点(10)- 网际协议IP及IPV6,IPV4详解

涉及知识点 分类的IP地址&#xff0c;子网划分&#xff0c;CIDR和路由汇聚&#xff0c;IPV4数据报格式&#xff0c;IPV6协议&#xff0c;软考网络管理员常考知识点&#xff0c;软考网络管理员网络安全&#xff0c;网络管理员考点汇总。 原创于&#xff1a;CSDN博主-《拄杖盲学…

Java的理论知识部分

文章目录 前言 一、Java的发展 1.1、Java的出现 1.2、Java官方网址 1.3、Java的平台 1.4、Java各版本新加的内容 1.5、java特点 1.6、Java的三种运行机制 1.7、Java的编译与运行 1.8、补充内容——华为鲲鹏jdk以及鲲鹏计算 二、面向对象程序编程 2.1、对象与类 2.2、Ja…

第一次安装cocoapods经历

先是执行&#xff1a;sudo gem install cocoapods 报错&#xff1a; ERROR: Error installing cocoapods: The last version of activesupport (> 5.0, < 8) to support your Ruby & RubyGems was 6.1.7.3. Try installing it with gem install activesupport -v…

无需麻烦,快速下载MySQL JDBC驱动程序!

如何提升你的MySQL数据库操作速度呢&#xff1f; 不必再费时寻找&#xff0c;我讲为你带来最简便、快速的MySQL JDBC驱动程序下载方法&#xff01; 无需繁琐步骤&#xff0c;轻松获取所需&#xff0c;让你的数据库操作更加流畅&#xff0c;事半功倍&#xff01;立即点击下载即…

高速数据采集专家--青翼8通道125MSPS 16位AD采集FMC子卡

青翼自研FMC129是一款8通道125MHz采样率16位AD采集FMC子卡&#xff0c;符合VITA57.1规范&#xff0c;可以作为一个理想的IO模块耦合至FPGA前端&#xff0c;8通道AD通过高带宽的FMC连接器&#xff08;HPC&#xff09;连接至FPGA从而大大降低了系统信号延迟。 该板卡支持板上可编…

【资料分享】Xilinx Zynq-7010/7020工业评估板规格书(双核ARM Cortex-A9 + FPGA,主频766MHz)

1 评估板简介 创龙科技TLZ7x-EasyEVM是一款基于Xilinx Zynq-7000系列XC7Z010/XC7Z020高性能低功耗处理器设计的异构多核SoC评估板&#xff0c;处理器集成PS端双核ARM Cortex-A9 PL端Artix-7架构28nm可编程逻辑资源&#xff0c;评估板由核心板和评估底板组成。核心板经过专业的…

常见排序及其改进方案

常见排序及其改进方案 快速排序 思想&#xff1a; 找到一个基准&#xff0c;通常来说选取左边第一个元素 定义中间变量temp接收基准值 两个哨兵i,j分别从数组左端、右端进行扫描 (a)先从右端开始扫描&#xff1a;哨兵j先从右端开始扫描&#xff0c;确保右端元素>基准值…

Collapse折叠面板(antd-design组件库)展示所有配置选项和onChange的作用

1.Collapse折叠面板 可以折叠/展开的内容区域。 2.何时使用 对复杂区域进行分组和隐藏&#xff0c;保持页面的整洁。 手风琴 是一种特殊的折叠面板&#xff0c;只允许单个内容区域展开。 组件代码来自&#xff1a; 分页 Pagination - Ant Design 3.本地验证前的准备 参考文章【…

Jmeter(二) - 从入门到精通 - 创建测试计划(Test Plan)(详解教程)

1.简介 上一篇文章已经教你把JMeter的测试环境搭建起来了&#xff0c;那么这一篇我们就将JMeter启动起来&#xff0c;一睹其芳容&#xff0c;首先我给大家介绍一下如何来创建一个测试计划&#xff08;Test Plan&#xff09;。 2.创建一个测试计划&#xff08;Test Plan&#x…

前端实现pdf,图片,word文件预览

前端实现文件预览功能 需求&#xff1a;实现一个在线预览pdf、excel、word、图片等文件的功能。 介绍&#xff1a;支持pdf、xlsx、docx、jpg、png、jpeg。 以下使用Vue3代码实现所有功能&#xff0c;建议以下的预览文件标签可以在外层包裹一层弹窗。 图片预览 iframe标签能够将…

Learning to cluster in order to transfer across domains and tasks (ICLR 2018)

Learning to cluster in order to transfer across domains and tasks (ICLR 2018) 摘要 这篇论文提出一个进行跨域/任务的迁移学除了习任务&#xff0c;并将其作为一个学习聚类的问题。除了特征&#xff0c;我们还可以迁移相似度信息&#xff0c;并且这是足以学习一个相似度…

Git的常用命令,及还原文件的指定版本,及分支管理

一.git 常用命令 1.创建一个空的Git仓库或重新初始化一个现有仓库 git init 2.执行 clone 命令默认会拉取远程仓库的所有内容 git clone 3.显示版本库和暂存区的状态 git status 4.将该文件添加到暂存区 git add . 5.将git add 之后文件在暂存区之后的提交 git commit -m 提…

【Java高级语法】(十七)Stream流式编程:释放流式编程的效率与优雅,狂肝万字只为全面讲透Stream流!~

Java高级语法详解之Stream流 1️⃣ 概念及特征2️⃣ 优势和缺点3️⃣ 使用3.1 语法3.2 常用API详解3.3 案例 4️⃣ 应用场景5️⃣ 使用技巧6️⃣ 并行流 ParallelStream&#x1f33e; 总结 1️⃣ 概念及特征 Java的Stream流是在Java 8中引入的一种用于处理集合数据的功能强大且…

chatgpt赋能python:PythonGUI编程简介

Python GUI编程简介 Python是一款功能强大的开源编程语言&#xff0c;在很多领域都有广泛的应用。与其他编程语言相比&#xff0c;Python具有易于学习、易于阅读和易于维护等优点&#xff0c;因此成为许多程序员选择的首选语言之一。Python还提供了许多各种类型的GUI库&#x…

HHU云计算期末复习(上)Google、Amazon AWS、Azure

文章目录 第一章 概论第二章 Google 云计算2.1 Google文件系统&#xff08;GFS&#xff09;2.2 MapReduce和Hadoop2.3 分布式锁服务Chubby2.4 分布式结构化数据表Bigtable存储形式主服务器子表Bigtable 相关优化技术 2.5 分布式存储系统MegastoreMegastoreACID语义基本架构核心…

圆和矩形是否有重叠

&#x1f388; 算法并不一定都是很难的题目&#xff0c;也有很多只是一些代码技巧&#xff0c;多进行一些算法题目的练习&#xff0c;可以帮助我们开阔解题思路&#xff0c;提升我们的逻辑思维能力&#xff0c;也可以将一些算法思维结合到业务代码的编写思考中。简而言之&#…

Android设计模式—桥接模式

1.桥接模式 桥接模式是一种结构型设计模式&#xff0c;它通过将抽象部分与实现部分分离来解耦。它使用接口作为桥梁&#xff0c;将一个抽象类与其实现类的代码独立开来&#xff0c;从而使它们可以各自独立地变化。桥接模式的核心思想是“组合优于继承”。 简单来讲&#xff0…

RecyclerView性能优化之异步预加载

RecyclerView性能优化之异步预加载 前言 首先需要强调的是&#xff0c;这篇文章是对我之前写的《浅谈RecyclerView的性能优化》文章的补充&#xff0c;建议大家先读完这篇文章后再来看这篇文章&#xff0c;味道更佳。 当时由于篇幅的原因&#xff0c;并没有深入展开讲解&…

【TCP/IP】广播 - 定义、原理及编程实现

目录 广播 广播的原理及形式 广播的编程与实现 套接字选项设置 发送者 接收者 拓展资料 广播 广播(Broadcast)是指封包在计算机网络中传输时&#xff0c;目的地址为网络中所有设备的一种传输方式。这里所说的“所有设备”也被限定在一个范围之中&#xff0c;这个范围被称…

Android 13(T) - binder阅读(4)- 使用ServiceManager注册服务2

上一篇笔记我们看到了binder_transaction&#xff0c;这个方法很长&#xff0c;这一篇我们将把这个方法拆分开来看binder_transaction做了什么&#xff0c;从而学习binder是如何跨进程通信的。 1 binder_transaction static void binder_transaction(struct binder_proc *proc…