算法Algorithm------动态规划DP

动态规划DP

“动态规划”,概述】
动态规划 (dynamic programming) 与分治方法相似,都是通过组合子问题的解来求解原问题(在这里, "programming"指的是一种表格法)
动态规划应用于子问题重叠的情况,即不同的子问题具有公共的子子问题(子问题的求解是递归进行的,将其划分为更小的子子问题),动态规划算法对每个子子问题只求解一次,将其解保存在一个表格中,从而无需每次求解一个子子问题时都重新计算,避免了不必要的计算工作。

什么时候考虑用动态规划

什么时候考虑动态规划/可以用动态规划求解的问题的特征/】应用动态规划方法求解的最优化问题应该具备的两个要素:最优子结构和子问题重叠。需要在给定约束条件下优化某种指标、问题可分解为彼此独立且离散的子问题时,考虑用动态规划算法。若一个问题的最优解,其内部包含的所有子问题解也必须最优,则该问题呈现了“最优子结构”,具有此结构特征的问题可能会使用动态规划。

求“最”优解问题(最大值和最小值):乘积最大子数组;最长回文子串;最长上升子序列

求可行性(True 或 False):
如果有这样一个问题,让你判断是否存在一条总和为 x 的路径(如果找到了,就是 True;如果找不到,自然就是 False),或者让你判断能否找到一条符合某种条件的路径,那么这类问题都可以归纳为求可行性问题,并且可以使用动态规划来解。零钱兑换问题;验证字符串s3 是否是由 s1 和 s2 交错组成的。

求方案总数:路径规划问题。比如说给定一个数据结构和限定条件,让你计算出一个方案的所有可能的路径,那么这种问题就属于求方案总数的问题。

极客时间
动态规划问题一定具备以下三个特征
1.重叠子问题:在穷举的过程中(比如通过递归),存在重复计算的现象;
2.无后效性:子问题之间的依赖是单向性的,某阶段状态一旦确定,就不受后续决策的影响;
3.最优子结构:子问题之间必须相互独立,或者说后续的计算可以通过前面的状态推导出来。

不理解这些题目为啥也符合以上特征:1049.最后一块石头的重量 II

重叠子问题

mine:用递归法解决斐波那契数列,将递归树画出来后,可以看出来是有重叠子问题的。

子问题图

当思考一个动态规划问题时,我们应该弄清所涉及的子问题及子问题之间的依赖关系,我们可以借助子问题图弄清所涉及的子问题及子问题之间的依赖关系。
子问题图是一个有向图,每个顶点唯一地对应一个子问题,若求子问题 x的最优解时需要直接用到子问题y 的最优解,那么在子问题图中就会有一条从子问题 x的顶点到子问题 y的顶点的有向边。例如,如果自顶向下过程在求解 x时需要直接递归调用自身来求解 y,那么子问题图就包含从x到y的一条有向边,有向边 (x, y) 表示当求解子问题 x时需要子问题 y的解。我们可以将子问题图看做自顶向下递归调用树的“简化版”或“收缩版“,因为树中所有对应相同子问题的结点合并为图中的单一顶点,相关的所有边都从父结点指向子图结点。
子问题图G=(V,E)的规模可以帮助我们确定动态规划算法的运行时间,由于每个子问题只求解一次,因此算法运行时间等于每个子问题求解时间之和,通常,一个子问题的求解时间与子问题图中对应顶点的度(出射边的数目)成正比,而子问题的数目等于子问题图的顶点数,因此,通常情况下,动态规划算法的运行时间与顶点和边的数量呈线性关系。

自底向上的动态规划方法处理子问题图中顶点的顺序为:对于一个给定的子问题 x, 在求解它之前求解邻接至它的子问题 y。
用图算法中的术语说,自底向上动态规划算法是按“逆拓扑序”或“反序的拓扑序”来处理子问题中的顶点,换句话说,对于任何子问题,直至它依赖的所有子问题均已求解完成,才会求解它。

我们可以用图算法中的术语“深度优先搜索”来描述(带备忘机制的)自顶向下动态规划算法处理子问题图的顺序。

最优子结构

最优子结构是什么】问题的最优解由相关子问题的最优解组合而成,而这些子问题可以独立求解。如果一个问题的最优解包含其子问题的最优解,我们就称此问题具有最优子结构性质。满足最优子结构的问题,可以考虑使用动态规划算法或者贪心算法。

证明:一个最优解应该存在最优子结构】“cut–and–paste”技术结合反证法:已知:最优解A包含结构B 证明B为最优的 证明过程:假设B不是最优的,那肯定还存在一个可实现子结构B的任务的最优子结构C,将C替换B,则会得到一个全新的解,这个解应该是最优的,和原结构最优矛盾

用动态规划方法求解最优化问题的第一步就是刻画最优解的结构。
如果一个问题的最优解包含其子问题的最优解,我们就称此问题具有最优子结构性质。某个问题是否适合应用动态规划算法,它是否具有最优子结构性质是一个好线索(当然,具有最优子结构性质也可能意味着适合应用贪心策略)。使用动态规划方法时,我们用子问题的最优解来构造原问题的最优解,因此,我们必须小心确保考察了最优解中用到的所有子
问题。

在发掘最优子结构性质的过程中,实际上遵循了如下的通用模式:
1.证明问题最优解的第一个组成部分是做出一个选择,例如,选择钢条第一次切割位置,选择矩阵链的划分位置等,做出这次选择会产生一个或多个待解的子问题。
2. 对于一个给定问题,在其可能的第一步选择中,你假定已经知道哪种选择才会得到最优解,你现在并不关心这种选择具体是如何得到的,只是假定已经知道了这种选择。
3. 给定可获得最优解的选择后,你确定这次选择会产生哪些子问题,以及如何最好地刻画子问题空间。
4.利用“剪切—粘贴"(cut-and-paste) 技术证明:作为构成原问题最优解的组成部分,每个子问题的解就是它本身的最优解。证明这一点是利用反证法:假定子问题的解不是其自身的最优解,那么我们就可以从原问题的解中“剪切"掉这些非最优解,将最优解"粘贴”进去,从而得到原 问题一个更优的解,这与最初的解是原问题最优解的前提假设矛盾。如果原问题的最优解包含多个子问题,通常它们都很相似,我们可以将针对一个子问题的"剪切—粘贴“论证方法稍加修改,用于其他子问题。


在尝试使用动态规划方法时要小心,要注意问题是否具有最优子结构性质。考虑下面两个问题,其中都是给定一个有向图G=(V,E)和两个顶点u,v∈V

无权 (unweighted) 最短路径:找到一条从u到v 的边数最少的路径。这条路径必然是简单路径,因为如果路径中包含环,将环去掉显然会减少边的数量。无权最短路径问题具有最优子结构性质,证明见书。(mine:这块先放过)

无权最长路径:找到一条从 u到v的边数最多的简单路径。这里必须加上简单路径的要求,因为我们可以不停地沿着环走,从而得到任意长的路径。
设无权最长简单路径问题不具有最优子结构性质,证明见书。(mine:这块先放过)


最优子结构见书《算法导论》第216页的细致讨论,先放过。

状态转移方程

动态规划的实现方法:

《算法导论》
动态规划有两种等价的实现方法
1.
第一种方法称为带备忘的自顶向下法( top-down with memoization)°。在自顶向下方法中借助备忘机制来充分利用子问题重叠特性。此方法仍按自然的递归形式编写过程,但过程会保存每个子问题的解(通常保存在一个数组或散列表中)。当需要一个子问题的解时,过程首先检查是否已经保存过此解。如果是,则直接返回保存的值,从而节省了计算时间;否则,按通常方式计算这个子问题。我们称这个递归过程是带备忘的(memoized),因为它“记住”了之前已经计算出的结果。
2.
第二种方法称为自底向上法(bottom-up method)。这种方法一般需要恰当定义子问题“规模”的概念,使得任何子问题的求解都只依赖于“更小的”子问题的求解,因而我们可以将子问题按规模排序,按由小至大的顺序进行求解,当求解某个子问题时,它所依赖的那些更小的子问题都已求解完毕,结果已经保存,每个子问题只需求解一次,当我们求解它(也是第一次遇到它)时,它的所有前提子问题都已求解完成。

带备忘的自顶向下法(递归)

通过画出递归树发现,递归过程是有重叠子问题的,则引入备忘录机制。

带备忘的自顶向下法(递归)的弊端:需要额申请存储空间;递归调用较耗费时间。

带备忘的自顶向下法(递归)并不能解决没有重复子问题的类型的题目。

自底向上法

如何设计动态规划算法

设计一个动态规划的四个步骤】

  1. 刻画一个最优解的结构特征。
  2. 递归地定义最优解的值。
  3. 计算最优解的值,通常采用自底向上的方法。
  4. 利用计算出的信息构造一个最优解。

步骤 1~3 是动态规划算法求解问题的基础。如果我们仅仅需要一个最优解的值,而非解本身,可以忽略步骤 。如果确实要做步骤 4, 有时就需要在执行步骤 的过程中维护一些额外信息,以便用来构造一个最优解。

“最优解的值”的“最优解”差别:前者是只给你结果,后者是会给你这个最优解的值对应的方案。

每种动态规划解决方案都涉及网格。单元格中的值通常就是你要优化的值。每个单元格都是一个子问题,因此你应考虑如何将问题分成子问题。解决问题的网格是什么样的,可以通过思考以下几个问题:单元格中的值是什么?如何将这个问题划分为子问题?网格的坐标轴是什么?

在这里插入图片描述
在这里插入图片描述

其他

思考:动态规划与分治法的异同】

动态规划和贪心法的区别】

“贪心算法”,它与动态规划有很多相似之处。特别是,能够应用贪心算法的问题也必须具有最优子结构性质。贪心算法和动态规划最大的不同在千,它并不是首先寻找子问题的最优解,然后在其中进行选择,而是首先做出一次“贪心”选择----在当时(局部)看来最优的选择 然后求解选出的子问题,从而不必费心求解所有可能相关的子问题。令人惊讶的是,在某些情况下这一策略也能得到最优解!

贪心、回溯、递归、动态规划,思维过程、联系

详细可看极客时间《动态规划面试宝典》前四节,作者通过分析"硬币找零"这个问题,循序渐进:贪心–>回溯—>递归—>动态规划,的思维过程。

动态规划求解最优化问题典例

动态规划求解最优化问题典例,说明】常见的、经典的、基础的、重点的,动态规划类型问题,有清晰的问题描述和解答过程。

钢条切割问题

给定一段长度为n英寸的钢条和一个价格表 pi(i=1, 2, …, n), 求切割钢条方案,使得销售收益ri最大。

如何将长钢条切割成短钢条,使得总价值最高?

自顶向下的递归求解方法】
我们将钢条从左边切割下长度为 i的一段,只对右边剩下的长度为 n-i的一段继续进行切割(递归求解),对左边的一段则不再进行切割,即问题分解的方式为:将长度为n 的钢条分解为左边开始一段,以及剩余部分继续分解的结果,可以得到一个递归公式,在此公式中,原问题的最优解只包含一个相关子问题(右端剩余部分)的解,而不是两个。
在这里插入图片描述
随着输入规模增大,这个程序耗费时间会爆炸性地增长。为什么CUT-ROD的效率这么差?原因在于,CUT-ROD反复地用相同的参数值对自身进行递归调用,即它反复求解相同的子问题,可以借助树形结构进行递归展开,可以发现。朴素递归算法之所以效率很低,是因为它反复求解相同的子问题。

加入了备忘机制的自顶向下的递归求解方法】

在这里插入图片描述

自底向上的求解方法】

在这里插入图片描述
长度为n的钢条的最优切割方案是由第一次切割后(如果最优切割方案需要进行切割)得到的两段钢条的最优切割方案组成的。

基于BOTTOM-UP-CUT-ROD算法基础,求得最优解的值对应的切割方案。】
见书209页


在这里插入图片描述

在这里插入图片描述
在这里插入图片描述


矩阵链乘法问题

dai看《算法导论》

如何给矩阵乘积式加括号,使得标量乘法次数最小?
如何用最少的标量乘法操作完成一个矩阵链相乘的运算。

实例"矩阵链乘求最优时间/最优括号方法"。由于矩阵乘法满足结合律,将矩阵乘积运用括号,实现不同的计算顺序,导致有不同的乘法次数。试图求出,如何“加括号”可使得矩阵的数量乘(标量乘)次数最少.


最长公共子串

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

01背包问题

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
对于背包问题,你先解决小背包(子背包)问题,再逐步解决原来的问题。
明确网格的行和列的含义,网格的各行为商品,各列为不同容量(1~4磅)的背包。网格最初是空的,从第一行开始,从最小的容量1开始填入,从一行的左到右开始填,逐步填充其中的每个单元格,可将我们的判断过程凝练成一个状态转移方程。如果再新来一个商品作为可供选择的商品,那么就相当于给网格又添加了一行,之前的表格仍然是可用的。
沿着网格的一列往下走,最大价值不会降低。
各行的排列顺序对结果无关紧要。
增加一件更小的商品将如何呢?需要修改网格,网格的列的单位需要更加细。
使用动态规划时,要么考虑拿走整件商品,要么考虑不拿,而没法判断该不该拿走商品的一部分。

01背包问题,还有一种只用一维dp数组的状态转移方程。详见《代码随想录》

0-1背包问题描述】
给你一个可放总重量为W的背包和N个物品,对每个物品,有重量w和价值v 两个属性,那么第i个物品的重量为w[i] ,价值为v[i]。现在让你用这个背包装物品,问最多能装的价值是多少?
示例:输入:W=5,N=3,w=[3,2,1],v=[5,2,3]
输出:8。解释:选择i=0和i=2这两件物品装进包,总重量4,最大价值8。

在这里插入图片描述

完全背包问题

完全背包问题,描述:给你一个可放总重量为W的背包和N个物品,对每个物品,有重量w和价值v两个属性,那么第 i个物品的重量为w[i],价值为v[i]。现在让你用这个背包装物品,每种物品都可以选择任意多个,问这个背包最多能装的价值是多少?
示例:输入:W=5,N=3,w=[3,2,1],v=[5,2,3]
输出:15。解释:当i=2时,选择5次,总价值为5*3=15。

LC.322.零钱兑换

用自底向上的方法求解】

直观理解版本:
在这里插入图片描述

在这里插入图片描述

基于标准版本的含有状态转移的版本】

在这里插入图片描述

回文子串个数

连续子数组最大和

最长回文子串

LC.5.最长回文子串

最长回文子序列

最长回文子序列,题目描述:给定一个字符串 s ,找到其中最长的回文子序列,并返回该序列的长度。
解法见《极客时间----动态规划面试宝典》第9讲。

最长公共子序列Longest Common Subsequence,LCS

最长公共子序列,即两个单词中都有的序列包含的字母数

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述


dai看《算法导论》中的最长公共子序列部分。

路径规划-----走格子

问题:一个机器人位于一个 m * n 网格的左上角 (起始点在下图中标记为“开始” ),机器人每次只能向下或者向右移动一步,现在机器人试图达到网格的右下角(在下图中标记为“结束”)。问总共有多少条不同的路径?

见第10讲。

在这里插入图片描述

求可行性------能否跳跃到终点

跳跃问题,问题描述】给出一个非负整数数组 A,你最初定位在数组的第一个位置。数组中的每个元素代表你在那个位置可以跳跃的最大长度。判断你是否能到达数组的最后一个位置。

见第10讲

极客时间《动态规划面试宝典》摘录

在这里插入图片描述

第四讲涉及知识点罗列:
动态规划问题的特征。最优子结构。状态转移方程。动态规划求解硬币找零问题。带备忘录的自顶向下和动态规划算法对比和讨论。解决动态规划面试问题的通用框架。

第五讲涉及知识点罗列:
理解:数据结构、算法、思想。
一个问题到底该不该用动态规划思想来解呢、动态规划问题的典型特点、

第六讲涉及知识点罗列:
0-1 背包问题的介绍、分析、求解。
用0-1背包解题思路求解LC.1049问题。
数学语言给出通用的动态规划的公式(Hou:很抽象,不不懂);

第七讲涉及知识点罗列:
完全背包问题的介绍、分析、代码求解、优化。

第八讲知识点罗列:
其他几个常见的动态规划问题。
子数组问题:回文子串个数问题的分析和求解、最大子数组之和问题的分析和求解、如何寻找状态和子问题、构建状态转移方程,并对其进行优化。

第九讲知识点罗列:
子序列问题:最长回文子序列、最长公共子序列

第十讲知识点罗列:
路径规划问题

第十一讲知识点罗列:
最长递增序列的问题;如何求解最长上升子序列的长度

第十二讲知识点罗列:
不重叠的子数组之和问题、最大子数组之积问题。子数组问题的解题模板。

第十三讲知识点罗列:
理解最优子结构

第十四讲知识点罗列:
面试环节常考的高频动态规划问题汇总】

线性类型的动态规划问题】
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

区间类型的动态规划问题】

背包问题】
在这里插入图片描述

方案总数问题】

在这里插入图片描述

复杂问题】
在这里插入图片描述
第十五、十六讲,课程回顾与总结】

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Abner_iii

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值