算法导论习题解-第15章动态规划

编号以第3版为准

#15.5-4 改进最优查找树

Knuth has shown that there are always roots of optimal subtrees such that root[i, j-1] <= root[i, j] <= root[i+1, j] for all 1 <= i <= n. Use this fact to modify the OPTIMAL-BST procedure to run in Θ(n^2) time.

证明:假设已经计算出了所有大小为4的子树以及相应的root,由题中结论知 root[1,4] <= root[2,5] <= root[3,6] ...。那么大小为5的子树根将会分布在上述元素之间,即 root[1,4] <= (root[1,5]) <= root[2,5] <= (root[2,6]) <= root[3,6] ...,括号中的是待计算的root。每个待探测的root只有有限个候选,所有待探测的root加起来不超过n。

#15-1 有向无环图中的最长简单路径

给定一个有向无环图G=(V, E),边权重为实数,给定图中两个定点s和t,求s到t的最长加权简单路径。

解:假设顶点已经经过了拓扑排序,取定点集a[1 .. n],其中a[1]=s,a[n]=t,子问题为LongestPath(a[1 .. i],代码如下:

#a is topological sorted, a[0] is s, a[-1] is t
def longest(a):
  #d[i] is the longest distance from a[0] to a[i]
  d = [-INF]*len(a)
  d[0] = 0
  for i in range(1, len(a)):
    for j in range(i):
      wji = weight(a[j], a[i])
      if wji != None and d[j] > -INF and d[j] + wji > d[i]:
        d[i] = d[j] + wji
  return d[-1]

#15-2 最长回文子序列

子序列不要求是连续的,比如character的最长回文子序列为carac。

解法1:O(n^3),利用最长公共子序列LCS来解,对每一个a[i],计算a[1 .. i]与a[n .. i+1]的LCS,以及a[1 .. i-1]与a[n .. i+1]的LCS+1。

解法2:O(n^2),子问题为a[i .. j]包含的最大回文,令L[i, j]为a[i .. j]之间最大的回文长度,如果a[i]=a[j]则L[i .. j] = 2 + L[i+1, j-1],否则L[i, j] = max(L[i+1, j], L[i, j-1])。参考 http://www.2cto.com/kf/201311/258795.html

注:如果要求子串是连续的,则存在O(n)的算法--Manacher算法,参考 http://www.felix021.com/blog/read.php?2040 和 http://blog.csdn.net/ggggiqnypgjg/article/details/6645824

#15-3 双调欧几里德旅行商问题

解:O(n^2)的算法见这里 http://blog.csdn.net/xiajun07061225/article/details/8092247 子问题比较难以想到,因为子问题不是闭合的线路,而原问题是闭合的。

一种利用闭合线路子问题的解法为O(n^3),考虑最左边(或最右边)的两个顶点,一定是在同一条路线上,考虑原问题t[1 .. n],如果存在两个连续的顶点i和i+1,使得i和i+1不在同一条线路上,则将子路线t[1 .. i+1]和t[i .. n]合并,并擦去线段t[i]-->t[i+1],即得到原问题的一条路线;如果不存在这样的顶点i和i+1,则路线为1-->2-->3...-->n-->1。为了得到原问题的最佳解,需要检查每一个i和i+1,子问题为t[i .. j],代码如下:

算法导论习题解-第15章动态规划_第1张图片

from math import sqrt
#O(n*n*n)
def bitonic(t):
    if len(t)==1:
        return 0
    if len(t)==2:
        return 2*dist(t[0], t[1])
    if len(t)==3:
        return dist(t[0],t[1]) + dist(t[1],t[2]) + dist(t[0],t[2])
    dmin = 0
    for i in range(1, len(t)):
        dmin += dist(t[i-1], t[i])
    dmin += dist(t[0], t[-1])
    for i in range(1, len(t)-2):
        d = bitonic(t[:i+2]) + bitonic(t[i:]) - 2*dist(t[i], t[i+1])
        if d < dmin:
            dmin = d
    print t, dmin
    return dmin

def dist(a, b):
    return sqrt((a[0]-b[0])**2 + (a[1]-b[1])**2)
    
t=[(0,6),(1,0),(2,3),(5,4),(6,1),(7,5),(8,2)]
bitonic(t)

#15-4 整齐打印

宽度固定为M的页面,打印一段文字,要求每行行尾的空格数量的三次方之和最小(最后一行除外)。

解:O(n^2),先不考虑最后一行的特殊处理,则子问题为a[1 .. i]的最佳打印,为计算a[1 .. i]的最佳打印只需考虑当前行是从单词i还是i-1, i-2, ..开始的。完整题目及解法见这里  http://blog.csdn.net/mishifangxiangdefeng/article/details/7921947

#15-6 公司聚会计划

Stewart教授是一家公司总裁的顾问,这家公司计划一个公司聚会。这个公司有一个层次结构;也就是,管理关系形成一棵以总裁为根的树。人事部给每个雇员以喜欢聚会的程度来排名,这是一个实数。为了使每个参加者都喜欢这个聚会,总裁不希望一个雇员和他(她)的直接上司同时参加。Stewart教授面对一颗描述公司结构的树,使用了10.4节描述的左子女、右兄弟表示法。树中每个节点除了包含指针,还包含雇员的名字以及该雇员喜欢聚会的排名。描述一个算法,它生成一张客人列表,使得客人喜欢聚会的程度的总和最大。分析你的算法的执行时间。

解:O(n^2),对每个节点T以及以T为根的子树,记录YES(T)为邀请T参加时的该子树的最大聚会值,NO(T)为T不参加时的该子树的最大聚会值。不管是计算YES(T)还是NO(T)都只需考虑T的直接孩子。

#15-8 基于接缝裁剪(seam carving)的图像压缩

m x n个像素的图像A,通过从每行删除一个像素来减少图片的大小,要求相邻的两行删除的像素必须在同一列或相邻的列,另外每个像素删除后对图像有一个破坏度,要求找一条从顶端至底端的删除曲线,破坏度最小。

解:O(m * n),从上到下逐行计算,假设目前在考虑第i行,令S[i, j]为以A[i, j]结尾的曲线里面最小的破坏度,则S[i, j]只取决于S[i-1, j-1], S[i-1, j], S[i-1, j+1]。

#15-9 字符串拆分

解:O(n^3),和矩阵链式乘法类似,设最终的字符串为a[1], a[2], ..., a[n],则第一次拆分可能在a[i]和a[i+1]之间,子问题为a[i .. j]。有没有效率更高的算法?

#15-10 投资策略规划

初始资金1万元,有n种股票,第i种股票在第j年的回报率为r[i,j],即年初的d元资金年底会变成d*r[i,j],未来10年的回报率都是已知的。每年年初有一次决定投资的机会,可以继续上一年的选择不变,也可以将资金重新分配到其它股票,如果选择不变需支付f1的手续费,重新分配需要f2的手续费,f1 < f2。目标是在10年末总收益最大。(a)题目允许每年将钱投入多支股票,请证明:存在最佳投资策略,每年将钱投入单一的股票 (b)给出最优算法。

解:(b) O(n*n*m),m为总年份。题目中对手续费的表述有歧义,是对每支股票收取还是一年只交一笔,另外手续费是另交还是从股票里扣除?简便起见,假设每支股票都需要交,且是直接扣除(其它假设对结论无影响)。每年只选择单一股票,令BEST(i, j)表示在第j年购买了股票i的情况下,j年底所能拥有的最大资本,则BEST(i, j) = max { (BEST(k, j-1) - (k==i ? f1 : f2)) * r[i,j] for all k }

(a) 对任意的多重投资方案B,设法找到单一投资方案A,使得每年末A的资本不少于B,且每年当B不改投时A也不改投,B改投时A不一定改投,这样A方案的手续费一定低于B方案。首先考虑B每年都改投的情况,A只需购买当年收益最高的股票即可,由数学归纳法可证每年年底A的资产都比B高。然后证明B有些年份不改投的情况,假设B在连续的y年内不改投,那么将这连续的y年合并成一个加长的年,每只股票的在“这一年”的回报率等于每年回报率的乘积,手续费是相应的倍数,如此处理之后就变成了每一“年”都改投的情况。

#15-11 库存规划

某公司,每月固定能生产m台设备,如需生产额外的设备,则每台额外设备的成本为c。已知未来n个月每个月的需求为d[i]。另外如果每个月末还库存j台设备,则需要付出h(j)的库存成本,h(j)是一个增函数。请安排每个月的生产计划。请安排每个月的生产计划,使得满足所有需求的前提下成本最小。

解:题目中有一点没说清楚,即每月是否可以生产少于m台设备,如果不可以的话,就很简单,每个月生产m台设备,如果还不能满足需求再生产额外设备即可。现在假设每月可以生产少于m台设备,这样可以在需求不足的时候节约库存成本。令D=d[1]+d[2]+...+d[n],子问题为在第i个月末库存为j台时的最低成本MIN_COST(i, j) = min { MIN_COST(i-1, k) + h(j) + (d[i]+j-k <= m ? 0 : c(d[i]+j-k)) for all k },效率O(D*D*n)。有没有效率更高的算法?

#15-12 签约棒球自由球员

有N个职位,每个职位有P个候选球员,每个球员有特定的签约费,以及球员价值。在总预算为X元的情况下,招聘总价值最大的球员。(有些职位可以留空)

解:类似0-1背包问题的动态规划解法。假设第i个位置的第j个球员的费用为F[i,j],价值V[i,j]。子问题为使用x元招聘前i个职位(可以留空)所能得到的最大价值MAX_VALUE(x, i) = max { MAX_VALUE(x-F[i,j], i-1) + V[i, j] for all j },效率为O(X*P*N)。注意:0-1背包问题的效率为O(X*N),每个物品只有0-1两种选择,而本题每个位置有P种选择。

#15-7 达到最高效益的调度

本题来自第2版。假设有一台机器,以及在此机器上处理的n个作业a[1 .. n]的集合。每个作业a[j]有一个处理时间t[j],效益p[j],以及最后期限d[j]。机器在一个时刻只能处理一个作业,而且作业a[j]必须在t[j]连续时间单位内不间断地运行。如果作业a[j]在最后期限d[j]之前完成,则获得效益p[j],但如果在最后期限之后才完成,则没有效益。请给出一个动态规划算法,来寻找能获得最大量效益的调度,假设所有的处理时间都是1到n之间的整数。

方便起见,假设作业已经根据最后期限排序,且每个作业的最后期限都不相同。TODO

你可能感兴趣的:(算法导论习题解-第15章动态规划)