前言
动态规划相信大家都不陌生,其重要性及实用性也不需要我过多强调。做为一种实际上代码并不长也不难写的思想,动规却对初学者不是那么“友好”。 不过事实上只要弄懂了动规思想的核心及其适用环境,像我这样的菜鸡也能搞定那些看似很无从下手的题目。那么,下面我就简单解说一下动态规划这种思想,并带大家看一下这种思想适用的经典例题。
基础知识及定义
核心思想
动态规划思想的本质就是所谓“重叠子问题”。
某些题目如果将其看作一整个问题来对待会十分的复杂繁琐,可如果适当地对其做拆解,把大问题分解成互相紧扣的小问题后再着手求解便会相对容易。于是动态规划就将子问题一个个递推求解,最终获得整个问题的解答。另外,与分治不同,动态规划有“记忆性”。只要我们进行填表的操作就可以避免重复计算,提高效率。
性质特点
动态规划问题的最大特点分别是最优子结构,重叠子问题,和后效性。
最优子结构
如果大问题的解中包含了子问题的最优解,那么就可以说这个问题具有最优子结构的性质。适合运用动规的题目具有这个性质,所以我们在解题时便可以将子问题的最优解构建起来组成整个问题的最优解。
重叠子问题
既然我们把动规的题目分解成了一个个子问题,那么这些子问题必须有一定的联系,能令我们使用程序一个个递归下去才会让我们有可能获得最终大问题的解。所以在动规思想中,如果一个大问题可以利用递归反复求解相同的子问题,那么这个大问题就具有重叠子问题的特性。
后效性
对于动规中的子问题,一个子问题到下一个子问题是通过递归实现的,所以上一个子问题的解对于下一个有直接影响,这种性质被称为具有后效性。
状态,状态转移方程
介绍完了核心思想及特性,我还需要解释一下状态和状态转移方程两个概念。
状态
状态就是大问题的子问题,没什么好说的QAQ
状态转移方程
前面说过子问题到下一个子问题会使用递归,那么状态转移方程就是上一个状态推导下一个状态时使用的方程。
例题
说清楚了入门知识点,我们来看一道超经典的例题: 01背包问题
具体为什么不讲全背包问题而是选择讲01背包的原因是因为 我懒 01背包比较好理解。
01经典背包问题
这个问题的背景是有一个背包和一堆物品,每个物品有不同的价值和重量,因为背包只有一定的容量,所以让我们求出在背包容重以内可取到的最大物品总价值。
好看的配图
解题思路
- 首先做子问题拆解
不难发现,此时可以将问题拆解为前i个物品在容量为j时可以取到的最大价值。 - 判断情况
如果物品直接大于背包承重,那么不考虑
如果物品重量小于承重且小于背包剩余容量,直接放入 - 状态转移方程(dp)
如果物品小于背包原承重却大于剩余容量,此时我们相当于可以拿出一定重量的其他物品来给其腾出空间。但有时,拿出一定重量的其他物品并用指定物品替换后价值反而没有原先高,所以我们需要进行判断。
(以下是状态转移方程,j