《算法导论》笔记(9) 动态规划 部分习题

来源:互联网 发布:php用apache还是nginx 编辑:程序博客网 时间:2024/05/17 09:11

一些本章的习题的解法简答及伪码。

15.1-5 斐波那契数的子问题图。每个斐波那契数与前两个斐波那契数有关。所以有N个顶点,2*(N-2)条边。


15.2-4 矩阵链乘法的子问题图。N个矩阵的子问题是N-1对,所以共有(N-1)*N/2与N*(N-1)*(N-2)/2条边。


15.3-6 外币兑换问题。必须是不重复的兑换。可以做一个集合S={D1, D2, D3…}代表兑换序列,sn=r12*r23*…*rn代表最终兑换金额,那么开始是{D1, Dn},然后每次加入一个新元素i,找到最佳的si。可以证明新加入的元素不改变原来序列的顺序,只需要找一个最佳位置插入。那么每次的子问题就是for(i=2 to n) find_best_sn。


15.4-5。最长单调子序列。若已知到第i位为止的最长单调子序列,考察第i+1位a[i+1],会发生什么情况?它能够续接到到那些末位<a[i+1]的子序列中,从而将这些子序列加长1。令A[i],B[i]分别代表S截至到第i位的最长单调子序列的长度和构成最长单调子序列的A[i]的前驱在S中的位置。

longest(S){  for(i = 1 to S.n){    for(j= 1 to i){      if(S[i]>S[j]){ A[i]=max(A[i], A[j]+1); if(A[i] changed) B[i]=j;}    }    }}


最差运行时间是O(n^2)。最长单调子序列的元素个数是A[n]即A[]的最后一项,B[i]是第i位作为最长单调子序列的最后一项时,其前驱项在S的位置。最长单调子序列是L:
longest_s(B){    x=(the first max element in A).index;  while(x) {y=S[x]; L.push(y); x=B[y]; }  return L;}

15.4-6 构造一个数组A[],A[i]表示长度为i的最长单调子序列的最后一项的最小值。S扫描到第S[j]项,找到S[j]是在A[]中的位置i并替换,此时S[j]是构成长度为i的最长单调子序列的最后一项最小值。因为S[j]是已知的i长度最长单调子序列的最后一项,所以插入不会影响已知的长度大于i的最长单调子序列,即A[]中排在i之后的所有项。

longest(S){  for(i =1 to S.n){    if(S[i]>A[A.length]) {A.push(S[i]); B[i]=A.length;}    else{      k=1; j=A.length;      while(k!=j) {if(S[i]<A[(j+k)/2]) j=(j-k)/2; elseif(S[i]>A[(j+k)/2]) k=(j-k)/2; }       A[j]=S[i]; B[i]=j;    }  }}


最差运行时间是O(n*lg(n))。S扫描一遍后,A的长度即是S的最长单调子序列。每次在A[]中替换S的第i个元素S[i]时,S[i]插入A[j]的位置j记录在B[i]中,那么B[]中最大值B[i]表明了最长单调子序列的最后一项是S[i],依次倒推,B[i]左侧与其最靠近的次大的B[k]值代表最长单调子序列的倒数第二项是S[k]。最长单调子序列是L:
longest_s(B){    x=(max element in B).index;  y=B[x];  while(x--){ if(B[x]==y) {L.push(S[x]); y--;}  }   return L;}



思考题 15-1 有向无环图的最长简单路径。维持一张二维的表格R,一个集合S,rij表明i到j点的最长路径,S表明已搜索的点。搜索与t点直接相连的所有点,这些点记为S1,在R中记录下相关的rij值,并将S1加入到S。检查S1有无互相连接,如果有,更新最大路径rij值。然后依次搜索每个与S1点直接相连的点S2,记录所有的S2到S1以及S2到t的路径rij。检查S2有无互相连接,如果有,更新最大路径rij值。下一步是与S2点直接相连的S3,此时相同方法检查S3到t的路径,并试图找到有无因S3内部相连致使最大路径更新。如此一步一步将所有点加入S后,可以得到s到t的最长简单路径。

search_longest(S){ while(S not null) {  find all A[i];  S=S-A[i];  for(j=i; j>0; j--) update distance of S[i] to S[j-1]: d(S[i, k], S[j-1, l])= max( d(S[i, k], S[j, r]) + d(S[j, r], S[j-1, l]) ) //任意k, r, l }}



思考题15-2 最长回文就是正序与逆序的最长公共子序列。


思考题15-3 双调欧几里德旅行商问题。首先确定最左点。然后定义子问题。记i是所有点按照x坐标从左到右排序后的序号,取任意不同两个点i, j,D(i, j)表示从i向左到头转身再到j的最短距离。那么当j<i-1时,D(i, j)=D(i, j-1)+ p(j-1, j),P(j-1, j)是j-1点到j点的直线距离。当j=i-1时,D(i, j)=min(D(k, j)+p(k, i)),其中1<=k<=j。最终结果D(n, n)=D(n-1, n)+ p(n-1, n)。

for(i=1 to n)  for(j= 2 to i-1)    update distance(i, j): if j<i-1 distance(i, j)=distance(i-1, j)+p(i, i-1); else distance(i, j)=distance(i-1, k)+ p(k, i)  //1<=k<=j total_distance=distance(n, n-1)


思考题15-6 每棵子树的评分总和是Rk=r1+...rn+b1*...bn*r0,ri表示第i个子结点的分数,bi表示第i个最佳子结点是否包含根。那么对于上一级子结点,最大分数是max(R1+R2...+Rn+b1*b2...*bn*R0), 从最底层开始做一个公司组织架构树,每个结点有2个值,表示此结点为根的子树的分数,根节点出现或不出现两种情况。然后向上汇总,得到上一层的每个结点的两个值。

for layer= n to 1:  update V[node].true, V[node].false; //true, false分别是node的根结点出席与不出席情况的社交值max(V[root].true, V[node].false)


思考题15-8 构造一张m*n的表格D[][]。从第一行开始,D[1, j]表示删除A[1, j]的破坏度,D[i, j]表示到运行到第i行i列删除A[i, j]后的最小破坏度。那么D[i, j]=d[i, j]+min(D[i-1, j-1], D[i-1, j], D[i-1, j+1]),从第1行运行到第n行,找到D[n]中的最小值。

for(i= 1 to m)  for(j= 1 to n)    D[i, j]=d[i, j]+min(D[i-1, j-1], D[i-1, j], D[i-1, j+1]); best_result=min(all elements in D[m])


思考题15-9 类似最优二叉搜索树问题。L[1...m]如果是乱序则排序,在此假设是已经排序的。分解过程类似二叉搜索树,分解一次变成2棵子树。则总花费时间为2棵子树各自的时间加上此次分解前的元素个数。C[i, j]是i到j之间最佳分解方案。则C[i, j]=min(C[i,r]+C[r+1,j])+(j-i+1),其中min(C[i,r]+C[r+1,j])表示分割点r的各种不同取法,前后两个子问题解的和的最小值。

for i=1 to m  for j= 1 to i+1   for r= i to j-i+1      C[i, j]=min( C[i, r]+C[r+1, j] , C[i, j] ) + (j-i+1)best result=C[1, m]


思考题15-10 首先,第i年的结束,上年总收益为d[i]=sum(r[i,1]*d[i,1]+r[i,2]*d[i,2]+...+r[i,j]*d[i,j]),则第i+1年的最佳配置是d[i+1]=sum(r[i+1,1]*d[i+1,1]+r[i+1,2]*d[i+1,2]+..+r[i+1,j]*d[i+1,j])-f2<=max(r[i])*d[i]-f或者d[i]-f1,当i=1时d[1]=max(r[1])*d0,可知每年的最佳策略是将所有的钱投入到单一投资中。且具有最有子结构性质。

具体算法是构造一个10*n表格,从第一年开始计算d[1, j]=d0*r[1, j],然后计算第二年d[2, j] = max(d[0])*r[1, j]-f2 或 d[i, j]-f1,依次上推。

for i= 1 to 10 for j = 1 to n   A[i, j]= max(A[i-1]) is in j colum? max(A[i-1])*r[i, j]- f2 : A[i-1, j]*r[i, j]- f1;best_result= max(A[10])


思考题15-12 签约棒球自由球员问题。其实是0-1背包问题的变型。遍历第i个位置的球员,此球员是否加入集合中替换现有相同位置要根据此球员价格和VORP,比较已知的相同总价下的最大总VORP来进行决策。其子问题是此位置之前的各种预算的最佳解。遍历所有球员和位置,可以得到全局最大V值的解。

for i= 1 to n  for i=1 to p    for each player at position i: S.add_player; V[i, k]= max( player.VORP + V[ i-1,  (k-player.price) ], V[i, k] ) //update best V[i] best_result=V[n]


                                             
0 0
原创粉丝点击