本章通过了较多个例子来说明动态规划。
1、动态规划问题的特征:
- 最优子结构
- 重叠子问题
2、动态规划解决问题的方法就是通过解决很多的小问题而解决大问题。因此,动态规划的效率将取决于两个因素:子问题的数量和子问题的解决效率。实际上,动态规划的时间效率就是:子问题的数量*子问题的时间效率。
以下是具体例子:
3、有向非循环图中的最短路径问题:
动态规划做法:
以S为源点,先拓扑排序,然后每个结点的最小距离等于它的所有前继结点加对应边值的最小值。
例:dist(D) = min {dist(B)+1, dist(C)+3 }
伪码:
不仅可以求最短路径,还可以求最长路径
4、最长增序列问题:给定一数列,找出这个数列中拥有最大元素个数的增序列。
例:5 2 8 6 3 6 9,则最长增序列为2 3 6 9,有4个元素构成
方案:首先建图:对于i<j , 若ai<aj ,则画一条ai指向aj的边
则此问题的解就是上面所提到的有向非循环图的最长路径问题的解。
伪码:
5、Edit distance问题:两个单词配对,使对应的不一样的字母最少,可以使用'-'。例,对于SNOWY和SUNNY,比较2种配对方案:
注:Edit distance is so named because it can also be thought of as the minimum number of edits—insertions, deletions, and substitutions of characters——needed to transform the first string into the second. For instance, the alignment shown on the left corresponds to three edits: insert U , substitute O → N , and delete W .
动态规划解法:
设两个单词分别为x[1...i] , y[1...j], 设原问题的解为E(i,j)
对于每个单词的最后一个字母,只有以下三种可能:
对于第一种情况,剩下的子问题为E(i-1,j)
对于第二种情况,剩下的子问题为E(i,j-1)
对于第二种情况,剩下的子问题为E(i-1,j-1)
综上,E(i,j) = min {1+E(i-1,j), 1+E(i,j-1), diff(i,j)+E(i-1,j-1)}
其中,若x[i] == y[i],则diff(i,j)=0;若x[i] != y[i],则diff(i,j) = 1。
边界情况:很显然,E(0,j) == j, E(i,0) == i
编程时需要维护一个2维数组,一行一行或一列一列填充该表格
一个具体例子+伪码:
6、背包问题:
简述:有下列几个物品,并且背包的最大负载是10,请问如何装背包才能装最大价值的物品?
先讨论两种最简单的类型:
- 重复背包问题(即完全背包问题,一个物品可以取无限次) 我们定义K(w) = 重量为w的背包可以取得的最大价值,现在的问题是,怎么把K(w)表示成子问题的形式? 答案是 对于简述中的那个具体实例,K(0)=0,K(1)=0, K(2)=9, K(3) = 14, K(4) = 18....... 写出了子问题表现形式,那么编程实现就相对容易。 注:If this application seems frivolous, replace “weight” with “CPU time” and “only W pounds can be taken” with “only W units of CPU time are available.” Or use “bandwidth” in place of “CPU time,” etc. The knapsack problem generalizes a wide variety of resource-constrained selection tasks.
- 非重复背包问题(即01背包问题,一个物品有且只有一个) 我们定义: 那么我们要得答案就是K(W,n) 怎么表达子问题?非常简单:物品j要么算上,要么不算上。所以: 编程时,同样需要维护一个二维数组 边界条件:K(0,j) = 0 , K(w,0) = 0 伪码:
7、Chain matrix multiplication
矩阵乘法不满足交换律,但满足结合律,所以A*B*C*D == A*(B*C)*D == .......
对于m*n的矩阵乘以n*p的矩阵,需要mnp次乘法运算
对于不同的结合情况,乘法的代价也是也不同的
一个具体例子:对于50*20的矩阵A, 20*1的矩阵B, 1*10的矩阵C, 10*100的矩阵D这4个矩阵的乘法:
问题是:通过动态规划来寻找最小代价。
假设我们要计算:,
每一个矩阵的行列为:
则我们定义:
其中C(i,i) == 0
可以写出子问题的表现形式:
以下是伪码:
8、最短可靠路径(Shortest reliable paths)
在网络中,求最短路径的时候也要求经历的边尽量少,目的是可以减少一些不确定因素
可以通过修改Dijkstra算法达到这一目的
我们令 dist(v,i) 是从s到v用了i条边的最短路径
初始时对于所有除了s的顶点,有dist(v,0)== 无穷,对于s,dist(s,0) == 0
写出子问题的表现形式:
问题就解决了。
9、所有顶点对的最短路径问题:
两种选择:
- 用|V|次dijkstra算法,复杂度为O(|V|*|V|*|E|)
- 动态规划:Floyd-Warshall算法,复杂度为O(|V|*|V|*|V|) 附:强烈对图论中的一些算法不太清楚的朋友推荐一个网站,上面有较多算法的动画演示:
10、数塔问题:有形如下图所示的数塔,从顶部出发,在每一结点可以选择向左走或是向右走,一直走到底层,要求找出一条路径,使路径上的值最大。
解决办法:
- 暴力,指数级算法,不可取
- 动态规划,自底向上