动态规划(Dynamic Programming)
动态规划属于运筹学范围。动态规划是求解决策过程最优化的数学方法。把多阶段过程转化为一系列单阶段问题,利用各阶段之间的关系,逐个求解,创立了解决这类过程优化问题的新方法——动态规划。
动态规划的问题之所以难,是因为它的其他算法不同,动态规划问题没有模板可以背诵。而且动态规划问题类型很多。总体来说,动态规划的问题有以下几类:
1、计数问题。典型题目有机器人走路。
2、求最大值最小值问题。典型问题有最长上升子序列。
3、存在性问题。典型问题有取石子游戏。
适用条件
动态规划问题必须满足以下的两个条件:
最优化原理
也就是最优子结构。一个最优化策略具有这样的性质,不论过去状态和决策如何,对前面的决策所形成的状态而言,余下的诸决策必须构成最优策略。简而言之,一个最优化策略的子策略总是最优的。一个问题满足最优化原理又称其具有最优子结构性质。
无后效性
将各阶段按照一定的次序排列好之后,对于某个给定的阶段状态,它以前各阶段的状态无法直接影响它未来的决策,而只能通过当前的这个状态。换句话说,每个状态都是过去历史的一个完整总结。这就是无后向性,又称为无后效性。
动态规划解题方法
1、自底向上
2、自顶向下
动态规划解题套路
动态规划解题可以分为 4 步:
第一步:确定状态。
第二步:状态转移方程。
第三步:初始条件和边界情况。
第四步:计算顺序。
下面我们使用一个简单的动态规划入门题来说明这四个步骤。
过程详解
问题描述:找零
我们有三种硬币,面值分别为:2 元、5 元和 7 元,而且每种硬币足够多。现在我们要购买一本价值为 27 元的书。如何用最小的硬币组合正好付清,而且不需要找零。
本题就是一个典型的求最大值最小值动态规划问题。
如果本题用贪心的方法,可以得到:先用 3 个 7 元,构成 21 元。剩下 6 元,再用 3 个 2 元构成。这样需要使用的硬币组合为:2+2+2+7+7+7=27,合计为 6 枚。
而正确的答案是:7+5+5+5+5=27,合计为 5 枚。
确定状态
状态是动态规划的核心。在动态规划解法中,我们都需要一个数组,数组可以是一维数组,可以是两维数组。数组的作用就是描述问题的状态。例如使用一维数组 dp[i] 表示第 i 步的状态,使用二维数组 dp[i][j] 表示第 i 步中第 j 部分状态。状态数组类似于我们描述数学问题中的未知量 x、y、z 等。
因此对于任何一个动态规划问题,我们首先需要确定状态。这里包含了两个子问题:
1、最后一步。虽然我们不能确定过程,但是每个问题都有最后的确定状态。
按例题找零为例,假设这个问题一定存在一个最优解,使用 K 枚硬币构成一个最优策略。
我们知道最后一步就是要将所有硬币组合总额编程 27。那么这 K 枚硬币可以描述为 A1,A2,...,AK,将这 K 枚硬币累加起来,可以得到,虽然我们不知道 Ai 的具体值。
那么最后一步就是这个最优策略中的最后一个决策,也就是最后一个硬币 。扣除最后一个硬币后,前面所有硬币的总和将是 ,如下图所示。
我们不需要关注前面 K-1 枚硬币是怎么样的组合,只需要保证拼出 即可。而且要保证拼出的硬币数量最小,也就是前面也是最优策略。这个很容易用反证法来证明。
2、子问题。
解决了最后一步(最少用多少枚硬币可以拼出 27-Ak)后,我们可以使用递归的思路,确定出子问题。这样,我们就将一个规模较大问题,分解为若干个问题一样规模较小子问题(Subproblem)。
这样,子问题就变成:最少用多少枚硬币可以拼出 X 元。
总结起来,就是通过描述最后一步,将一个规模大的问题,分解为若干个相同的而且规模较小的子问题。
状态转移方程
到目前为止,我们还是不知道最后一枚硬币 Ak 到底是多少。不要急。根据题目,我们知道硬币的可能只有 2 元、5 元和 7 元。那么最后一枚硬币也只能是这三种硬币中的一种。
这样,我们可以看出一个一维数组可以描述这个状态转移的过程。我们假设 dp[k] 表示拼出 K 元的最优策略,也就是 dp[1] 表示拼出 1 元的最优策略,dp[27] 表示拼出 27 元的最优策略。
因此,根据最后一步的描述,我们可以知道,要拼出 27 元,我们有三种可能:
1、使用 2 元拼出 27 元,对应的状态转移方程就是 dp[27]=dp[27-2]+1,也就是 dp[25] 加上一枚 2 元硬币。
2、使用 5 元拼出 27 元,对应的状态转移方程就是 dp[27]=dp[27-5]+1,也就是 dp[22] 加上一枚 5 元硬币。
3、使用 7 元拼出 27 元,对应的状态转移方程就是 dp[27]=dp[27-7]+1,也就是 dp[20] 加上一枚 7 元硬币。
除此之外,没有其他可能。题目要求我们求出最优策略,也就是最小的可能性,因此对应的 dp[27] 为取这三个方法的最小值,即。
这样,我们使用 dp[x] 表示最少硬币拼出 x 元的最优策略,我们可以得出整个问题的状态转移方程。
其实本问题就是使用自顶向下的方法来解决。
初始条件和边界情况
到这里为止,我们写出了题目的状态方程,下面我们需要确定该方程的初始条件和边界情况。
边界条件
y 元不能拼出,我们定义 dp[y] 为正无穷大。例如 。这样我们可以写出边界条件如下:
初始条件
从状态转移方程第递推式中,我们需要给出状态方程的初始条件。在状态方程中,我们可以看出只需要给出一个初值就可,也就是 dp[0]。对应的初始条件如下:
这样一个完整的状态转移方程,我们就可以写出如下所示:
计算顺序
根据上面推导出的状态转移方程,我们可以非常容易知道,计算的顺序应该是 1、2、3、...、K。也就是使用一个 for 循环就可以解决问题。
这样,我们就可以非常轻松的解决这个例题。对于其他动态规划的题目,我们都可以通过同样的步骤来完成,只需要多加练习即可。