第15章 动态规划

最后更新于:2022-04-01 07:35:49

# 一、综述 动态规划是通过组合子问题的解而解决整个问题的。 动态规划适用于子问题不是独立的情况,也就是各子问题的包含公共的子子问题。 动态规划对每个子问题只求解一次,将其结果保存在一张表中。 动态规划通常用于最优化问题。 动态规划的设计步骤:a.描述最优解的结构b.递归定义最优解的值c.按自底向上的方式计算最优觖的值d.由计算出的结构构造一个最优解 # 二、代码 ### 15.1装配线调度 ~~~ #include <iostream> using namespace std; #define I 2 #define J 6 int a[I+1][J+1], t[I+1][J+1], e[I+1], x[I+1], n = J;//输入 int f[I+1][J+1], l[I+1][J+1],rf,rl; //输出 //书上的伪代码 void Fastest_Way() { //初始化 f[1][1] = e[1] + a[1][1]; f[2][1] = e[2] + a[2][1]; int j; for(j = 2; j <= n; j++) { //根据公式15.6计算 if(f[1][j-1] + a[1][j] <= f[2][j-1] + t[2][j-1] + a[1][j]) { //记录计算结果 f[1][j] = f[1][j-1] + a[1][j]; l[1][j] = 1; } else { //记录计算结果 f[1][j] = f[2][j-1] + t[2][j-1] + a[1][j]; l[1][j] = 2; } //根据公式15.7计算 if(f[2][j-1] + a[2][j] <= f[1][j-1] + t[1][j-1] + a[2][j]) { //记录计算结果 f[2][j] = f[2][j-1] + a[2][j]; l[2][j] = 2; } else { //记录计算结果 f[2][j] = f[1][j-1] + t[1][j-1] + a[2][j]; l[2][j] = 1; } } //最后一个结果另外记录 if(f[1][n] + x[1] <= f[2][n] + x[2]) { rf = f[1][n] + x[1]; rl = 1; } else { rf = f[2][n] + x[2]; rl = 2; } } //输出 void Print_Stations() { cout<<"输出装配路线"<<endl; int i = rl, j; //从后向前输出 cout<<"line "<<i<<" station "<<n<<endl; for(j = n; j > 1; j--) { //获取记录的结果 i = l[i][j]; cout<<"line "<<i<<" station "<<j-1<<endl; } } //练习 //15.1-1 void Print_Stations2(int i, int j) { cout<<"顺序输出装配路线"<<endl; if(j != n) i = l[i][j+1]; //先输出前面的 if(j > 1) Print_Stations2(i, j-1); //再输出当前的 cout<<"line "<<i<<" station "<<j<<endl; } //输入数据 void Input() { int i, j; cout<<"输入在装配站S(i,j)的装配时间a(i,j):"<<endl; //7 9 3 4 8 4 8 5 6 4 5 7 for(i = 1; i <= I; i++) { for(j = 1; j <= J; j++) cin>>a[i][j]; } cout<<"输入进入装配线i的花费时间e(i):"<<endl; //2 4 for(i = 1; i <= I; i++) cin>>e[i]; cout<<"输入从S(i,j)站移动S(i2,j+1)的花费时间t(i,j):"<<endl; //2 3 1 3 4 2 1 2 2 1 for(i = 1; i <= I; i++) { for(j = 1; j < J; j++) cin>>t[i][j]; } cout<<"输入从装配线i离开的花费时间x(i):"<<endl; //3 2 for(i = 1; i <= I; i++) cin>>x[i]; } void Output() { int i, j; cout<<"输出f[i][j]"<<endl; //f[i][j]表示第j个站是在装配线i上完成的,完成1到j的装配最少需要的时间 for(i = 1; i <= I; i++) { for(j = 1; j <= J; j++) cout<<f[i][j]<<' '; cout<<endl; } cout<<"输出l[i][j]"<<endl; //l[i][j]表示使得f[i][j]最小时在哪个装配线上装配j-1 for(i = 2; i <= I; i++) { for(j = 1; j <= J; j++) cout<<l[i][j]<<' '; cout<<endl; } } int main() { Input(); Output(); Fastest_Way(); Print_Stations(); Print_Stations2(rl, J); return 0; } ~~~ ### 15.2矩阵链乘法 ~~~ #include <iostream> using namespace std; #define N 6 int m[N+1][N+1] = {0}, s[N+1][N+1] = {0}; void Matrix_Chain_Order(int *p) { int i, l, j, k, q; for(i = 1; i <= N; i++) m[i][i] = 0; for(l = 2; l <= N; l++) { for(i = 1; i <= N - l + 1; i++) { j = i + l - 1; m[i][j] = 0x7fffffff; for(k = i; k <= j - 1; k++) { q = m[i][k] + m[k+1][j] + p[i-1]*p[k]*p[j]; if(q < m[i][j]) { m[i][j] = q; s[i][j] = k; } } } } } //15.2-2递归算法 int Matrix_Chain_Order(int *A, int start, int end) { //只有一个矩阵时,不需要括号 if(start == end) return 0; //如果已经有结果,直接使用结果 if(m[start][end]) return m[start][end]; int i, q; m[start][end] = 0x7fffffff; //P199,公式15.15 for(i = start; i < end; i++) { q = Matrix_Chain_Order(A, start, i) + Matrix_Chain_Order(A, i+1, end) + A[start-1] * A[i] * A[end]; //选最小值 if(q < m[start][end]) { m[start][end] = q; s[start][end] = i; } } return m[start][end]; } //输出结果 void Print_Optimal_Parens(int *A, int i, int j) { if(i == j) cout<<'A'<<i; else { cout<<'('; Print_Optimal_Parens(A, i, s[i][j]); Print_Optimal_Parens(A, s[i][j]+1, j); cout<<")"; } } int main() { int A[N+1] = {5, 10, 3, 12, 5, 50, 6}; int A2[N+1] = {30, 35, 15, 5, 10, 20, 25}; Matrix_Chain_Order(A, 1, N); Print_Optimal_Parens(A, 1, N); return 0; } ~~~ ### 15.3动态规划基础 ~~~ #include <iostream> using namespace std; #define N 6 int m[N+1][N+1] = {0}, s[N+1][N+1] = {0}; //重叠子问题 int Recursive_Matrix_Chain(int *p, int i, int j) { //只有一个矩阵时,不需要括号 if(i == j) return 0; //如果已经有结果,直接使用结果 if(m[i][j]) return m[i][j]; int k, q; m[i][j] = 0x7fffffff; //P199,公式15.15 for(k = i; k < j; k++) { q = Recursive_Matrix_Chain(p, i, k) + Recursive_Matrix_Chain(p, k+1, j) + p[i-1] * p[k] * p[j]; //选最小值 if(q < m[i][j]) { m[i][j] = q; s[i][j] = k; } } return m[i][j]; } //做备忘录 int Lookup_Chain(int *p, int i, int j) { int k, q; if(m[i][j] < 0x7fffffff) return m[i][j]; if(i == j) m[i][j] = 0; else { for(k = i; k < j; k++) { q = Lookup_Chain(p, i, k) + Lookup_Chain(p, k+1, j) + p[i-1]*p[k]*p[j]; if(q < m[i][j]) { m[i][j] = q; s[i][j] = k; } } } return m[i][j]; } int Medorized_Matrix_Chain(int *p) { int n = N, i, j; for(i = 1; i <= n; i++) { for(j = i; j <= n; j++) m[i][j] = 0x7fffffff; } return Lookup_Chain(p, 1, n); } //输出结果 void Print_Optimal_Parens(int *p, int i, int j) { if(i == j) cout<<'A'<<i; else { cout<<'('; Print_Optimal_Parens(p, i, s[i][j]); Print_Optimal_Parens(p, s[i][j]+1, j); cout<<")"; } } int main() { int A[N+1] = {5, 10, 3, 12, 5, 50, 6}; int A2[N+1] = {30, 35, 15, 5, 10, 20, 25}; //重叠子问题 memset(s, 0, sizeof(s)); cout<<Recursive_Matrix_Chain(A, 1, N)<<endl; Print_Optimal_Parens(A, 1, N); cout<<endl; //做备忘录 memset(s, 0, sizeof(s)); cout<<Medorized_Matrix_Chain(A)<<endl; Print_Optimal_Parens(A, 1, N); cout<<endl; return 0; } ~~~ ### 15.4最长公共子序列 ~~~ #include <iostream> using namespace std; #define M 8 #define N 9 int b[M+1][N+1] = {0}, c[M+1][N+1] = {0}; int c2[2][M+1] = {0}; /****书上的伪代码***********************/ void Lcs_Length(int *x, int *y) { int i, j; //初始化 for(i = 1; i <= M; i++) c[i][0] = 0; for(j = 1; j <= N; j++) c[0][j] = 0; //根据公式15.14计算 for(i = 1; i <= M; i++) { for(j = 1; j <= N; j++) { //记录计算结果 if(x[i] == y[j]) { c[i][j] = c[i-1][j-1] + 1; b[i][j] = 2; } else { if(c[i-1][j] >= c[i][j-1]) { c[i][j] = c[i-1][j]; b[i][j] = 1; } else { c[i][j] = c[i][j-1]; b[i][j] = 3; } } } } } //输出 void Print_Lcs(int *x, int i, int j) { if(i == 0 || j == 0) return; if(b[i][j] == 2) { Print_Lcs(x, i-1, j-1); cout<<x[i]<<' '; } else if(b[i][j] == 1) Print_Lcs(x, i-1, j); else Print_Lcs(x, i, j-1); } //15.4-2 不使用表b的情况下计算最LCS并输出 void Lcs_Length2(int *x, int *y) { int i, j; //初始化 for(i = 1; i <= M; i++) c[i][0] = 0; for(j = 1; j <= N; j++) c[0][j] = 0; //求LCS的时间没有什么区别,只要把与b有关的去掉就可以了 for(i = 1; i <= M; i++) { for(j = 1; j <= N; j++) { //第一种情况 if(x[i] == y[j]) c[i][j] = c[i-1][j-1] + 1; else { //第二种情况 if(c[i-1][j] >= c[i][j-1]) c[i][j] = c[i-1][j]; //第三种情况 else c[i][j] = c[i][j-1]; } } } } //区别在于输出,根据计算反推出前一个数据,而不是通过查找获得 void Print_Lcs2(int *x, int i, int j) { //递归到初始位置了 if(i == 0 || j == 0) return; //三种情况,刚好与Lcs_Length2中的三种情况相对应(不是按顺序对应) //第二种情况 if(c[i][j] == c[i-1][j]) Print_Lcs2(x, i-1, j); //第三种情况 else if(c[i][j] == c[i][j-1]) Print_Lcs2(x, i, j-1); //第一种情况 else { //匹配位置 Print_Lcs2(x, i-1, j-1); cout<<x[i]<<' '; } } //15.4-3备忘录版本,类似于递归,只是对做过的计算记录下来,不重复计算 //每一次迭代是x[1..m]和y[1..n]的匹配 int Lcs_Length3(int *x, int *y, int m, int n) { //长度为0,肯定匹配为0 if(m == 0|| n == 0) return 0; //若已经计算,直接返回结果 if(c[m][n] != 0) return c[m][n]; //公式15.14的对应 if(x[m] == y[n]) c[m][n] = Lcs_Length3(x, y, m-1, n-1) + 1; else { int a = Lcs_Length3(x, y, m-1, n); int b = Lcs_Length3(x, y, m, n-1); c[m][n] = a > b ? a : b; } return c[m][n]; } //15.4-4(1)使用2*min(m,n)及O(1)的额外空间来计算LCS的长度 void Lcs_Length4(int *x, int *y) { int i, j; //c2是2*min(M,N)的矩阵,初始化 memset(c2, 0 ,sizeof(c2)); //类似于上文的循环,只是i%2代表当前行,(i-1)%2代表上一行,其余内容相似 for(i = 1; i <= N; i++) { for(j = 1; j <= M; j++) { if(y[i] == x[j]) c2[i%2][j] = c2[(i-1)%2][j-1] + 1; else { if(c2[(i-1)%2][j] >= c2[i%2][j-1]) c2[i%2][j] = c2[(i-1)%2][j]; else c2[i%2][j] = c2[i%2][j-1]; } } } //输出结果 cout<<c2[N%2][M]<<endl; } void Lcs_Length5(int *x, int *y) { int i, j, temp = 0; memset(c2, 0 ,sizeof(c2)); for(i = 1; i <= N; i++) { for(j = 1; j <= M; j++) { if(y[i] == x[j]) c2[i%2][j] = c2[(i-1)%2][j-1] + 1; else { if(c2[(i-1)%2][j] >= c2[i%2][j-1]) c2[i%2][j] = c2[(i-1)%2][j]; else c2[i%2][j] = c2[i%2][j-1]; } } } cout<<c2[N%2][M]<<endl; } void Print() { int i, j; for(i = 1; i <= M; i++) { for(j = 1; j <= N; j++) cout<<c[i][j]<<' '; cout<<endl; } } int main() { int x[M+1] = {0,1,0,0,1,0,1,0,1}; int y[N+1] = {0,0,1,0,1,1,0,1,1,0}; Lcs_Length(x, y); // Print(); Print_Lcs(x, M, N); // Lcs_Length2(x, y); // Lcs_Length3(x, y, M, N); // Print(); // Print_Lcs2(x, M, N); // Lcs_Length4(x, y); return 0; } ~~~ ### 15.5最优二叉查找树 ~~~ #include <iostream> using namespace std; #define N 7 double e[N+2][N+2] = {0};//期望 double w[N+2][N+2] = {0};//概率 int root[N+2][N+2] = {0};//记录树的根结点的位置 /*****调试过程中用于输出中间信息***************************/ void PrintE() { int i, j; for(i = 1; i <= N+1; i++) { for(j = 1; j <= N+1; j++) cout<<e[i][j]<<' '; cout<<endl; } cout<<endl; } void PrintW() { int i, j; for(i = 1; i <= N+1; i++) { for(j = 1; j <= N+1; j++) cout<<w[i][j]<<' '; cout<<endl; } cout<<endl; } void PrintRoot() { int i, j; for(i = 1; i <= N+1; i++) { for(j = 1; j <= N+1; j++) cout<<root[i][j]<<' '; cout<<endl; } cout<<endl; } /****书上的伪代码对应的程序****************************/ //构造最做树 void Optimal_Bst(double * p, double *q, int n) { int i, j, r ,l; double t; //初始化。当j=i-1时,只有一个虚拟键d|i-1 for(i = 1; i <= n+1; i++) { e[i][i-1] = q[i-1]; w[i][i-1] = q[i-1]; } //公式15.19 for(l = 1; l <= n; l++) { for(i = 1; i <= n-l+1; i++) { j = i+l-1; e[i][j] = 0x7fffffff; //公式15.20 w[i][j] = w[i][j-1] + p[j] + q[j]; for(r = i; r <= j; r++) { //公式15.19 t = e[i][r-1] + e[r+1][j] + w[i][j]; //取最小值 if(t < e[i][j]) { e[i][j] = t; //记录根结点 root[i][j] = r; } } } } } /****练习**********************/ //15.5-1输出最优二叉查找树 void Construct_Optimal_Best(int start, int end) { //找到根结点 int r = root[start][end]; //如果左子树是叶子 if(r-1 < start) cout<<'d'<<r-1<<" is k"<<r<<"'s left child"<<endl; //如果左子树不是叶子 else { cout<<'k'<<root[start][r-1]<<" is k"<<r<<"'s left child"<<endl; //对左子树递归使用Construct_Optimal_Best Construct_Optimal_Best(start, r-1); } //如果右子树是叶子 if(end < r+1) cout<<'d'<<end<<" is k"<<r<<"'s right child"<<endl; //如果右子树不是叶子 else { cout<<'k'<<root[r+1][end]<<" is k"<<r<<"'s right child"<<endl; //对右子树递归使用Construct_Optimal_Best Construct_Optimal_Best(r+1, end); } } int main() { int n = N; // double p[N+1] = {0, 0.15, 0.10, 0.05, 0.10, 0.20}; // double q[N+1] = {0.05, 0.10, 0.05, 0.05, 0.05, 0.10}; double p[N+1] = {0, 0.04, 0.06, 0.08, 0.02, 0.10, 0.12, 0.14}; double q[N+1] = {0.06, 0.06, 0.06, 0.06, 0.05, 0.05, 0.05}; Optimal_Bst(p, q, n); // PrintE(); // PrintW(); // PrintRoot(); cout<<'k'<<root[1][N]<<" is root"<<endl; Construct_Optimal_Best(1, N); return 0; } ~~~ # 三、练习 ### 15.1装配线调度 ### 15.1-1 ~~~ //15.1-1 void Print_Stations2(int i, int j) { cout<<"顺序输出装配路线"<<endl; if(j != n) i = l[i][j+1]; //先输出前面的 if(j > 1) Print_Stations2(i, j-1); //再输出当前的 cout<<"line "<<i<<" station "<<j<<endl; } ~~~ ### 15.1-4 不使用l数组来记录,输出时根据类似L4-L13的比较来计算出前一个station的数据 ### 15.2矩阵链乘法 ### 15.2-1 算法:类似于15.3的做备忘录 运行结果:((A1A2)((A3A4)(A5A6))) ### 15.2-2 ~~~ //15.2-2递归算法 int Matrix_Chain_Order(int *A, int start, int end) { //只有一个矩阵时,不需要括号 if(start == end) return 0; //如果已经有结果,直接使用结果 if(m[start][end]) return m[start][end]; int i, q; m[start][end] = 0x7fffffff; //P199,公式15.15 for(i = start; i < end; i++) { q = Matrix_Chain_Order(A, start, i) + Matrix_Chain_Order(A, i+1, end) + A[start-1] * A[i] * A[end]; //选最小值 if(q < m[start][end]) { m[start][end] = q; s[start][end] = i; } } return m[start][end]; } ~~~ ### 15.3动态规划基础 ### 15.3-1 枚举的时间的复杂度是O(4^n)/(n^(3/2)) RECURSIVE-MATRIX-CHAIN的时间复杂度是O(n*3^(n-1)) 显然后者更有效 ### 15.3-3 解释见1楼 ~~~ #include <iostream> using namespace std; #define N 6 int m[N+1][N+1] = {0}, s[N+1][N+1] = {0}; //15.2-2递归算法 int Matrix_Chain_Order(int *A, int start, int end) { //只有一个矩阵时,不需要括号 if(start == end) return 0; //如果已经有结果,直接使用结果 if(m[start][end]) return m[start][end]; int i, q; m[start][end] = -1; //P199,公式15.15 for(i = start; i < end; i++) { q = Matrix_Chain_Order(A, start, i) + Matrix_Chain_Order(A, i+1, end) + A[start-1] * A[i] * A[end]; //选最小值 if(q > m[start][end]) { m[start][end] = q; s[start][end] = i; } } return m[start][end]; } //输出结果 void Print_Optimal_Parens(int *A, int i, int j) { if(i == j) cout<<'A'<<i; else { cout<<'('; Print_Optimal_Parens(A, i, s[i][j]); Print_Optimal_Parens(A, s[i][j]+1, j); cout<<")"; } } int main() { int A[N+1] = {5, 10, 3, 12, 5, 50, 6}; int A2[N+1] = {30, 35, 15, 5, 10, 20, 25}; Matrix_Chain_Order(A, 1, N); Print_Optimal_Parens(A, 1, N); return 0; } ~~~ ###  15.4最长公共子序列 ### 15.4-1 1 0 0 1 1 0 ### 15.4-2 ~~~ //15.4-2 不使用表b的情况下计算最LCS并输出 void Lcs_Length2(int *x, int *y) { int i, j; //初始化 for(i = 1; i <= M; i++) c[i][0] = 0; for(j = 1; j <= N; j++) c[0][j] = 0; //求LCS的时间没有什么区别,只要把与b有关的去掉就可以了 for(i = 1; i <= M; i++) { for(j = 1; j <= N; j++) { //第一种情况 if(x[i] == y[j]) c[i][j] = c[i-1][j-1] + 1; else { //第二种情况 if(c[i-1][j] >= c[i][j-1]) c[i][j] = c[i-1][j]; //第三种情况 else c[i][j] = c[i][j-1]; } } } } //区别在于输出,根据计算反推出前一个数据,而不是通过查找获得 void Print_Lcs2(int *x, int i, int j) { //递归到初始位置了 if(i == 0 || j == 0) return; //三种情况,刚好与Lcs_Length2中的三种情况相对应(不是按顺序对应) //第二种情况 if(c[i][j] == c[i-1][j]) Print_Lcs2(x, i-1, j); //第三种情况 else if(c[i][j] == c[i][j-1]) Print_Lcs2(x, i, j-1); //第一种情况 else { //匹配位置 Print_Lcs2(x, i-1, j-1); cout<<x[i]<<' '; } } ~~~ ### 15.4-3 ~~~ //15.4-3备忘录版本,类似于递归,只是对做过的计算记录下来,不重复计算 //每一次迭代是x[1..m]和y[1..n]的匹配 int Lcs_Length3(int *x, int *y, int m, int n) { //长度为0,肯定匹配为0 if(m == 0|| n == 0) return 0; //若已经计算,直接返回结果 if(c[m][n] != 0) return c[m][n]; //公式15.14的对应 if(x[m] == y[n]) c[m][n] = Lcs_Length3(x, y, m-1, n-1) + 1; else { int a = Lcs_Length3(x, y, m-1, n); int b = Lcs_Length3(x, y, m, n-1); c[m][n] = a > b ? a : b; } return c[m][n]; } ~~~ ### 15.4-4 (1)使用2*min(m,n)及O(1)的额外空间来计算LCS的长度 因为这里只需要求长度,而不需要求序列,可以只存储需要的内容。每一次的计算c[i][j]只与c[i-1][j]、c[i][j-1]、c[i-1][j-1]有关,所以只保留第i行和第i-1行 ~~~ //15.4-4(1)使用2*min(m,n)及O(1)的额外空间来计算LCS的长度 void Lcs_Length4(int *x, int *y) { int i, j; //c2是2*min(M,N)的矩阵,初始化 memset(c2, 0 ,sizeof(c2)); //类似于上文的循环,只是i%2代表当前行,(i-1)%2代表上一行,其余内容相似 for(i = 1; i <= N; i++) { for(j = 1; j <= M; j++) { if(y[i] == x[j]) c2[i%2][j] = c2[(i-1)%2][j-1] + 1; else { if(c2[(i-1)%2][j] >= c2[i%2][j-1]) c2[i%2][j] = c2[(i-1)%2][j]; else c2[i%2][j] = c2[i%2][j-1]; } } } //输出结果 cout<<c2[N%2][M]<<endl; } ~~~ (2)使用min(m,n)项以及O(1)空间 ~~~ //15.4-4(2)使用min(m,n)及O(1)的额外空间来计算LCS的长度 void Lcs_Length4(int *x, int *y) { int i, j; //c2是min(M,N)的矩阵,初始化 memset(c2, 0 ,sizeof(c2)); //类似于上文的循环,只是i%2代表当前行,(i-1)%2代表上一行,其余内容相似 int t1 = 0, t2; for(i = 1; i <= N; i++) { t2 = c[j]; for(j = 1; j <= M; j++) { if(y[i] == x[j]) c2[j] = t1 + 1; else c2[j] = max(c2[j], c2[j-1]); t1 = t2; } } //输出结果 cout<<c2[M]<<endl; } ~~~ ### 15.4-5 ~~~ #include <iostream> #include <string> using namespace std; #define N 10 int c[N+1] = {0};//c[i]表示A[1..i]的最长递增子序列 int pre[N+1];//pre[i]表示若要得到A[1..i]的最长递增子序列,i的前一个是哪个 //求最长递增子序列 void Length(int *A) { int i, j; //A[i] = max{A[j]+1} if A[j]>A[i] and j<i for(i = 1; i <= N; i++) { //初始化 c[i] = 0; pre[i] = 0; for(j = 0; j < i; j++) { if(A[i] > A[j]) { if(c[j] + 1 > c[i]) { c[i] = c[j]+1; pre[i] = j; } } } } cout<<c[N]<<endl; } //若要输出A[1..n]中的最长单调子序列,先输出A[1..pre[n]]中的最长单调子序列 void Print(int *A, int n) { if(pre[n]) Print(A, pre[n]); cout<<A[n]<<' '; } int main() { //因为从第开始记数,所以数组中的第一个数不算,只是占一个位置 // int A[N+1] = {0,1,2,3,4,5,6,7,8,9,10}; int A[N+1] = {0,11,2,13,4,15,6,17,8,19,10}; Length(A); Print(A, N); return 0; } ~~~ ### 15.1最优二叉查找树 ### 15.5-1 ~~~ //15.5-1输出最优二叉查找树 void Construct_Optimal_Best(int start, int end) { //找到根结点 int r = root[start][end]; //如果左子树是叶子 if(r-1 < start) cout<<'d'<<r-1<<" is k"<<r<<"'s left child"<<endl; //如果左子树不是叶子 else { cout<<'k'<<root[start][r-1]<<" is k"<<r<<"'s left child"<<endl; //对左子树递归使用Construct_Optimal_Best Construct_Optimal_Best(start, r-1); } //如果右子树是叶子 if(end < r+1) cout<<'d'<<end<<" is k"<<r<<"'s right child"<<endl; //如果右子树不是叶子 else { cout<<'k'<<root[r+1][end]<<" is k"<<r<<"'s right child"<<endl; //对右子树递归使用Construct_Optimal_Best Construct_Optimal_Best(r+1, end); } } ~~~ ### 15.5-2 k5 is root k2 is k5's left child k1 is k2's left child d0 is k1's left child d1 is k1's right child k3 is k2's right child d2 is k3's left child k4 is k3's right child d3 is k4's left child d4 is k4's right child k6 is k5's right child d5 is k6's left child k7 is k6's right child d6 is k7's left child d7 is k7's right child ### 15.5-4 ~~~ //15.5-4 void Optimal_Bst(double * p, double *q, int n) { int i, j, r ,l; double t; //初始化。当j=i-1时,只有一个虚拟键d|i-1 for(i = 1; i <= n+1; i++) { e[i][i-1] = q[i-1]; w[i][i-1] = q[i-1]; } //公式15.19 for(l = 1; l <= n; l++) { for(i = 1; i <= n-l+1; i++) { j = i+l-1; e[i][j] = 0x7fffffff; //公式15.20 w[i][j] = w[i][j-1] + p[j] + q[j]; //root[i,j-1] <= root[i,j] <= root[i+1,j] for(r = root[i][j-1]; r <= root[i+1][j]; r++) { //公式15.19 t = e[i][r-1] + e[r+1][j] + w[i][j]; //取最小值 if(t < e[i][j]) { e[i][j] = t; //记录根结点 root[i][j] = r; } } } } } ~~~ # 四、思考题 ### 15-1 双调欧几里德旅行商问题 见[算法导论-15-1-双调欧几里得旅行商问题](http://blog.csdn.net/mishifangxiangdefeng/article/details/7918983) ### 15-2 整齐打印 见[算法导论-15-2-整齐打印](http://blog.csdn.net/mishifangxiangdefeng/article/details/7921947) ### 15-3 编辑距离 见[算法导论-15-3-编辑距离](http://blog.csdn.net/mishifangxiangdefeng/article/details/7925025) ### 15-4 计划一个公司聚会 见[算法导论-15-4-计划一个公司聚会](http://blog.csdn.net/mishifangxiangdefeng/article/details/7930830) ### 15-6 在棋盘上的移动 见[算法导论-15-6-在棋盘上移动](http://blog.csdn.net/mishifangxiangdefeng/article/details/7931438) ### 15-7 达到最高效益的调度 见[算法导论-15-7-达到最高效益的调度](http://blog.csdn.net/mishifangxiangdefeng/article/details/7932349)
';