一、简述
动态规划是一种功能非常强大的计算模式,其解决问题的方式是首先定义它的一组子问题,然后按照由小到大,以小问题的解答支持大问题求解的模式,依此解决所有的子问题,并最终得到原问题(最大子问题)的解答
二、最长递增子序列(Longest Increasing Sequence)
1 .问题描述:输入是一个数字序列, 所谓子序列,是指在以上序列中按顺序选出的一个子集,形如,其中.如果子序列中的数字都是严格单调递增的,则称其为一个递增子序列。目标是找出原序列的一个最长子序列。
2. 解决思路:
- 为了更好地理解该问题的解空间,我们可以对任意两个可能在某递增子序列中存在递进关系的元素和,(即同时满足和),增加一条连接二者对应节点的有向边,这样原问题就变成在DAG中找最长路径。
- 令是以为终点的最长路径---对应着最长递增子序列的长度。可以看出,所有到达j的路径必然经过j的某个前驱节点,因此应该等于所有前驱对应的最长递增子序列长度的最大值加1.而如果没有可以到达的边,我们取为0.
(存在子问题间的一种排序以及如下的关联关系:对于任意一个子问题这种关联关系说明了如何在给定相对其较小的子问题的解的前提下,求得该子问题的解) - 状态转移方程:
for j = 1,2...n: L(j) = 1 + max{L(i) : (i,j) belongs to E} return max{L(j)}
三、编辑距离(Edit distance)
1 . 问题描述:利用两个字符串能在多大程度上相互对齐得到两个字符串的距离。
2. 解决思路:
- 首先定义子问题:考虑这两个字符串的前缀(prefix)。可令为两个字符串与之间的编辑距离。则前缀和的编辑距离就可以表示为
- 接着考虑和的最佳对齐,此时该对齐中最右侧的列只能是如下三种情况之一:
或 或
第一种情况:该列产生的情况是1,余下的是和的距离问题,对应子问题
第二种情况:同上,对应子问题
第三种情况:如果,则代价为1,如果,则代价为0;剩下的子问题是 - 可得状态转移方程:
diff(i,j) = 0 当且仅当 - 求解算法
for i =0,1,2...m: E(i,0) = i for j = 1,2,...m: E(0,j) = j for i = 1,2,..m: for j = 1,2,...n: E(i,j) = min{E(i-1,j)+1, E(i,j-1)+1 , E(i-1,j-1)+diff(i,j)} return E(m,n)
四、背包问题(Knapsack problem)
1. 问题描述:假设某人的背包最多能装磅,一共件物品,分别重,价值.怎样的组合才能使他带走的价值最高。
2. 多副本的背包问题:
- 子问题定义:考虑容量较小的背包,假设是容量可以容纳的最高价值。
- 状态转移方程:
- 算法:
K(0) = 0 for w = 1 to W: K(w) = max{K(w - wi) + vi : wi <= w} return K(w)
3. 单副本的背包问题:
- 子问题定义:单副本条件下不知道该物体有没有被使用过了,因此需要增加一个参数。是基于背包容量w和物品1,...j所能得到的最高价值,现在的目标是求
- 状态转移方程:要么需要选择物品以获得最高价值,要么不需要。
- 算法:
initialize all K(0,j) = 0 and all K(w,0)=0 for j = 1 to n: for w = 1 to W: if wi > w: K(w,j) = K(w,j-1) else: K(w,j) = max{K(w , j-1), K(w - wj, j-1) + vj} return K(W,n)
算法需要填写一个二维的表格,其中共有W+1行和n+1列。填写这个表格仅需要常数时间。
五、矩阵链式相乘(Chain Matrix Multiple)
1. 问题描述:矩阵乘法次序不同会导致运行时间的巨大不同。假设我们需要计算,其中每个矩阵的维数分别为,求最优的计算次序
2. 求解思路:
- 最优子问题:令是计算的最小代价
- 状态转移方程:
- 算法:
for i = 1 to n: C(i,i) = 0 for s = 1 to n-1: //s代表子问题的规模 for i = 1 to n-s: j = i + s C(i,j) = min{C(i,k) + C(k+1,j) + mi-1*mk*mj : i <= k < j} return C(1,n)
六、 最短路径问题(Short Path Problem)
1. 问题描述:求一个简单无向带权图中所有顶点的最短路径。---基于动态规划的Floyd-Warshall算法。
2. 求解思路:
- 最优子问题:记为仅仅允许使用作为中间节点时到的最短路径长度。初始条件下,当存在直接连接和的边时,为该边的长度,否则为无穷
- 考虑当在中间节点集中加入一个新的顶点时, 我们需要对所有的节点对,检查是否使用作为中间节点会得到更短的路径。到的使用了和其他编号较小中间节点的最短路径最多经过一次,此时只需要将同作比较,就能知道使用是否能够在和间得到一条更短的路径。
- 算法:
for i = 1 to n: for j = 1 to n: dist(i,j,0) = MAX for all (i,j) belongs to E: dist(i,j,0) = arr[i][j] for k = 1 to n: for i = 1 to n: for j = 1 to n: dist(i,j,k) = min{dist(i,k,k-1)+dist(k,j,k-1) , dist(i,j,k-1)}
七、最长公共子串问题(Longest Common Sequence)
1. 问题描述:Given two sequences x[1..m] and y[1..n], find a(not the) longest subsequence common to them both.
2. 解决思路:
- 最优子问题:考虑前缀,它代表着串和的最长公共子串的长度
- 状态转移方程:
- 算法:
for i = 0 to m: C(i,0) = 0 for j = 0 to n: C(0,j) = 0 //DP一定要有基础条件 if x[i] == y[j]: C(i,j) = C(i-1,j-1) + 1 else: C(i,j) = max{C(i-1,j),C(i,j-1)}