目录
1. 动态规划问题的基本要素
(1)最优子结构性质
当问题的最优解包含了子问题的最优解时,我们称问题具有最优子结构性质。
在动态规划算法中,利用问题的最优子结构性质,以自底向上的方式递归的从子问题的最优解逐步构造出整个问题的最优解。
(2)重叠子问题
动态规划问题利用了子问题的重叠性质,对每一个子问题只求解一次,然后保存到一个表格中,当再次需要解此问题时,知识简单的用常数时间查看一下结果。
2. 动态规划算法的基本步骤
(1)找出最优子结构性质,并刻画其结构特征
(2)递归地定义最优值
(3)以自底向上的方式计算最优值
(4)根据计算最优值时得到的信息构造最优解
3. 动态规划问题举例-最长公共子序列问题
问题定义
给定两个序列X={x1,x2,….,xm}和Y={y1,y2,….,yn},当另一序列Z既是X的子序列有事Y的子序列时,称Z是序列X和Y的公共子序列。
最长公共子序列问题:给定两个序列X和Y,找出X和Y的最长公共子序列。
最长公共子序列的结构
最长公共子序列问题具有最优子结构性质。
设序列X={x1,x2,….,xm}和Y={y1,y2,….,yn}的最长公共子序列为Z={z1,z2,….,zk},则
(1)若xm=yn,则zk=xm=yn,且Zk-1是Xm-1和Yn-1的最长公共子序列
(2)若xm!=yn且zxk!=xm,则Z是Xm-1和Y的最长公共子序列
(3)若xm!=yn且zxk!=yn,则Z是X和Yn-1的最长公共子序列
子问题的递归结构
首先要建立子问题最优值的递归关系。用c[i][j]记录序列Xi和Yj的最长公共子序列的长度。其中Xi={x1,x2,….,xi};Yj={
y1,y2,….yj}。当i = 0或j = 0时,空序列是Xi和Yj的最长公共子序列,故此时c[i][j] = 0。在其他情况下,最优子结构性质可建立如下的关系
c[i][j] = 0 i = 0, j = 0
=c[i - 1][j - 1] + 1 i, j > 0; xi = yj
=max{c[i][j - 1], c[i - 1][j]} i , j > 0; xi != yj
计算最优值
计算最长公共子序列的动态规划算法LCSLength以序列X={x1,x2,….,xm}和Y={y1,y2,….,yn}作为输入,输出两个数组c和b。其中c[i][j]存储Xi和Yjde最长公共子序列的长度,b[i][j]记录c[i][j]的值是由哪一个子问题的解得到的,这在构造最长公共子序列时会用到。
void LCSLength(int m, int n, char *x, char *y, int **c, int **y){
int i, j;
for (i = 0; i < m; i++) c[i][0] = 0;
for (j = 0; j < n; j++) c[0][j] = 0;
for (i = 0; i < m; i++){
for (j = 0; j < n; j++){
if (x[i] == y[j]){
c[i][j] = c[i-1][j-1] + 1;
b[i][j] = 1;
}else if (c[i-1][j] >= c[i][j-1]){
c[i][j] = c[i-1][j];
b[i][j] = 2;
}else{
c[i][j] = c[i][j-1];
b[i][j] = 3;
}
}
}
}
构造最长公共子序列
由算法LCSLength计算得到的数组b可用于快速构造序列X={x1,x2,….xm}和Y={y1,y2,….yn}的最长公共子序列。首先从b[m][n]开始,根据值在b中搜索。当b[i][j] = 1时,表示Xi和Yj的最长公共子序列是由Xi-1和Yj-1的最长公共子序列在尾部加上xi所得到的。当b[i][j] = 2时,表示Xi和Yj的最长公共子序列与Xi-1和Yj的最长公共子序列相同。当b[i][j] = 3时,表示Xi和Yj的最长公共子序列与Xi和Yj-1的最长公共子序列相同。
算法LCS实现根据b的内容打印输出X和Y的最长公共子序列。
void LCS (int i, int j, char *x, char *b){
if (i == 0 || j == 0) return;
if (b[i][j] == 1){
LCS(i - 1, j - 1, x, b);
cout << x[i];
}else if (b[i][j] == 1){
LCS(i - 1, j , x, b);
}else{
LCS(i, j - 1, x, b);
}
}
参考文献
王晓东. 《计算机算法设计与分析(第四版)》. 电子工业出版社. 44-54