算法导论14 Problems 答案
14-1
如果从节点\(s\)取\(t\)的路径最长化,那么子问题就相当于从\(s\)的后继节点\(u\)去到\(t\)的路径最长化,并且再添加上从\(s\)到\(v\)的路径。
子问题图就是\((V-\{s\},E-\{(s,v,w)\mid v \in V\})\).
令\(d(G,s,t)\)表示从图\(G=(V,E)\)中的\(s\)点到\(t\)点的最长路径,那么可以写出状态转移方程:
\[d(G,s,t)=\max_{(s,v,w) \in E}\{d((V-\{s\},E-\{(s,v,w)\mid v \in V\}),v,t)+w\}\]
其中,\(d(G,t,t)=0\),因为不需要移动。
由于删除的点的集合数量是幂指增长的,并且可能每个顶点都有出边,因此这个算法的时间复杂度为\(O(2^{|V|}\cdot |E|)\)。
可以使用拓扑排序,优化上述状态转移方程,将其降低至\(O(|V|+|E|)\)的时间复杂度。此时不需要涉及对图的操作。
14-2
如果一个回文字符串两边拼上两个相同的字符,那么得到了一个新的回文字符串,其长度增加了\(2\);如果一个字符串两边的字符串不相同,那么它的最长回文子序列一定是来自内部的子字符串。因此,\(w[i,j]\)的最优子结构是\(w[i+1,j-1],w[i+1,j],w[i,j-1]\)。令\(w[i,j]\)表示子字符串\(X[i:j]\)的最长回文子序列。那么可以写出\(w\)的状态转移方程:
\(w[i,j]= \left \{\begin{aligned} &0 & & \text{if}\quad i>j \\ &1 & & \text{if}\quad i=j \\ &w[i+1,j-1]+2 & & \text{if}\quad i<j\land X[i]=X[j]\\ &\max(w[i+1,j],w[i,j-1]) & & \text{if}\quad i<j\land X[i]\neq X[j]\\ \end{aligned}\right.\)
计算出表\(w\)的所有值即可。根据第4, 5行的2个嵌套循环不难得知,这个算法的时间复杂度为\(\Theta(n^2)\)。
1 | LPS-SOLUTION(X, m) |
14-3
首先需要对这些点的\(x\)坐标进行排序,那么我们可以考虑把这一些点拆分成两部分,组成两条不相交的有向路径,但是公用起点和终点。那么令\(d[i, j]\)表示这两条不相交路径的终点分别为\(i\)和\(j\)时(假设\(i> j\)),从起点到点\(i\)和从起点到点\(j\)这两条路径的最短长度之和。
那么对于状态\(d[i,j]\)而言,考虑每次添加一个右边的点\(i+1\)进来。添加时,这个点可以添加到\(i\)后面,那么变成状态\(d[i+1,j]\),也可以添加到\(j\)后面,变成状态\(d[i+1,i]\)。
那么,可以写出\(d\)的状态转移方程:
\(d[i,j]= \left \{\begin{aligned} &\text{dis}(p_i,p_j) & & \text{if}\quad i=2\land j=1 \\ &d[i-1,j] + \text{dis}(p_i,p_j) & & \text{if}\quad i > 2\land j < i - 1 \\ &\min_{k=1}^{i-2} \{d[i-1,k] + \text{dis}(p_k,p_j)\} & & \text{if}\quad i > 2\land j = i - 1 \\ \end{aligned}\right.\)
因此,最终还需要使两条路线指定相同的终点,那么最终答案为
\[\min_{i=1}^{n-2}\{d[n-1,i]+\text{dis}(p_n,p_{n-1})+\text{dis}(p_n,p_i)\}\]
子程序POINT-DISTANCE(A, B)
用于计算两个点的欧几里得距离,此处忽略实现过程。
假设计算两点间距离POINT-DISTANCE(A, B)
的时间复杂度为\(O(1)\)。观察程序BITONIC-TSP
第6行的嵌套循环不难得知,这个程序的时间复杂度为\(\Theta(n^2)\)。
1 | BITONIC-TSP(p, n) |
14-4
由于最后一行字符并不考虑后面的空格,因此我们考虑从后往前进行考虑。那么最优子问题就是将单词序列\(l_i,l_{i+1},\dots,l_n\)打印出来需要的额外空格立方之和。
如果后面这些单词只需要一行打印出来,那么就没有额外的空格(因为第一行就是最后一行)。否则,枚举\(j\ge i\),并且单词序列\(l_i,l_{i+1},\dots,l_j\)都能在一行打印出来,余下的空格数立方和与打印\(l_{j+1},l_{j+2},\dots,l_n\)时的最优决策时的值相加即可。
令状态\(f[i]\)表示将单词序列\(l_i,l_{i+1},\dots,l_n\)打印出来需要的额外空格立方之和,其中\(\displaystyle{s[i,j]=j-i+\sum_{k=i}^j l_k}\)表示如果打印单词序列\(l_i,l_{i+1},\dots,l_j\)时所需要的字符数。那么可以写出状态转移方程\(f\):
\(f[i]= \left \{\begin{aligned} &0 & & \text{if}\quad s[i,n]\le M \\ &\min_{i\le j\le n, s[i,j]\le m}\{ (M-s[i,j])^3+f[j+1]\} & & \text{if}\quad s[i,n]\le M \\ \end{aligned}\right.\)
如果\(s[i,j]\)的任意值可以以\(\Theta(1)\)的时间计算出来,那么根据程序PRINTING-NEATLY-SOLUTION
的第5行循环,可以观察出这个算法的时间复杂度为\(\Theta(n^2)\)。
1 | CAL-CHARACTERS(l, r, S) |
14-5
a
这里的子问题设计成:已经处理完了字符串\(X[1:i],Y[1:j]\)时所需要的最小花费。
按照这6种操作的定义,直接从前往后转移即可。
1 | EDIT-DISTANCE(X, Y, m, n, QC, QR, QD, QI, QT, QK) |
b
禁用旋转和终止操作,也就是令\(Q_T=Q_K=+\infty\)。
仅仅使用剩下四个操作,有\(Q_C = -1,Q_I= Q_D = 2,Q_R = 1\)。
其中,插入和删除操作对应两个字符含有空格的规则;复制对应两个字符相同的规则;替换对应两个字符不相同的规则。
最终,如下调用- EDIT-DISTANCE(X, Y, m, n, -1, 1, 2, 2, QT, QK)
这个程序即可得到最大的分数,注意前面有一个负号。
14-6
对于每一个员工\(x\),他可以进行两种决策:要么出席,要么缺席。以\(x\)为子树的所有员工决策满足最优子结构性质。以\(x\)为子树的出席最优决策,取决于\(x\)的所有孩子的子树的最优决策。
如果\(x\)出席,那么\(x\)的下属都不出席;如果\(x\)不出席,那么\(x\)的下属出不出席并没有限制,那么选择其中最优的一个选择即可。
因此,分别令\(f_{\text{attend}}(x),f_{\text{absent}}(x)\)表示当\(x\)出席/缺席时,以\(x\)为子树能够获得的最大分数和。那么不难写出状态转移方程为:
\(\begin{aligned} &f_{\text{attend}}(x)=\sum_{y \text{ is child of }x} f_{\text{absent}}(y)\\ &f_{\text{absent}}(x)=x.\texttt{rating}+\sum_{y \text{ is child of }x} \max\{f_{\text{absent}}(y),f_{\text{attend}}(y)\}\\ \end{aligned}\)
因此如果根节点为\(r\),最终结果为根节点\(\max\{f_{\text{absent}}(r),f_{\text{attend}}(r)\}\)。
由于每个节点恰好都被遍历了一次,并且其余操作都是常数操作,因此整个算法的时间复杂度为\(\Theta(n)\)。
1 | PLANNING-A-COMPANY-PARTY(x) |
14-7
a
考虑状态\(d[i,j]\)表示是否存在一条从\(v_0\)开始,长度为\(i\),最终到达节点\(j\)的路径。并且,这一条路径的权值和sigma[1 : i]
完全匹配。如果长度为\(i-1\)的路径是匹配的,并且符号相同,那么长度为\(i\)的路径也必定是匹配的。
那么不难写出关于\(d\)的状态转移方程:
\(d[i,j]= \left \{\begin{aligned} &1 & & \text{if}\quad i=0 \land j=v_0\lor i>0\land (\exists u\in V,(u,j)\in E\land\sigma(u,j)=\sigma_i\land d[i-1,u]=1) \\ &0 & & \text{if}\quad i=0 \land j\neq v_0\lor i>0\land\lnot (\exists u\in V,(u,j)\in E\land\sigma(u,j)=\sigma_i\land d[i-1,u]=1) \\ \end{aligned}\right.\)
最终,如果\(G\)是采用邻接表实现的,那么为了方便转移,此时使用图\(G\)的转置\(G^T\)进行转移。那么根据程序VITERBI-PATH
第5行的内循环可以得知,进行一次的时间为\(\Theta(|E|)\),外加外循环\(\Theta(k)\),最终整个算法的时间复杂度为\(\Theta(k\cdot |E|)\)。
1 | VITERBI-PATH(G, v0, sigma, k) |
b
只需要多记录一个状态参数prob[i, j]
,表示这条路径如果存在的最大概率,转移时只需要顺带维护状态值即可。
由于只是多新建了一个表,单次转移的过程仍然是\(\Theta(1)\),因此整个算法的时间复杂度仍为\(\Theta(k\cdot |E|)\)。
1 | VITERBI-PATH'(G, v0, sigma, k) |
14-8
a
由于\(n>1\),因此如果在第\(1\)列,有两个选项朝下方选择:正下方或者是右下方;如果是在第\(n\)列,那么可以选择正下方或者是左下方;否则,左下方,正下方,右下方都可以选择。无论在哪一行哪一列,都至少有\(2\)个选项选择,因此一个图像的接缝数的渐进增长满足\(\Omega(2^m)\),即指数级别。
b
令状态\(f[i,j]\)表示已经删除了前\(i\)行的一个像素,其中删除的第\(i\)行的像素为\(j\)时,已经累积的破坏度。
那么按照题意,每一个状态仅能从\(f[i-1,j-1],f[i-1,j],f[i-1,j+1]\)三个之一转移而来,不难写出状态转移方程:
\(f[i,j]= \left \{\begin{aligned} &d[i,j] & & \text{if}\quad i=1 \\ &\min\{f[i-1,j],f[i-1,j+1]\}+d[i,j] & & \text{if}\quad i>1 \land j = 1\\ &\min\{f[i-1,j-1],f[i-1,j]\}+d[i,j] & & \text{if}\quad i>1 \land j = m\\ &\min\{f[i-1,j-1],f[i-1,j],f[i-1,j+1]\}+d[i,j] & & \text{if}\quad i>1 \land 1 < j < m\\ \end{aligned}\right.\)
删完最后一行的像素后,得到最终结果:
\[\min_{j=1}^n\{f[m,j]\}\]
由于可以转移的状态最多为\(3\)个,因此根据第5,
6行的嵌套循环可知算法IMAGE-COMPRESSION
的时间复杂度为\(\Theta(n,m)\)。
1 | IMAGE-COMPRESSION(A, d, m, n) |
14-9
为了方便描述和编写这个算法,数组\(L\)末端会添加一个新元素\(n\),也就是字符串\(s\)的长度。
这个问题的子问题是将字符串\(s[L[i-1]+1:L[j]]\)按照\(L[i:j]\)所展示的那样划分,如果这个划分是最优的,那么由最优子结构的问题组合成的原问题也是最优的。
令\(f[i,j]\)表示将\(s[L[i-1]+1:L[j]]\)按照\(L[i:j]\)划分时,所需要的最小代价。那么考虑从\([i,j)\)中间某个点\(k\)进行切分。可以写出关于\(f\)状态转移方程:
\(f[i,j]= \left \{\begin{aligned} &d[i,j] & & \text{if}\quad i=j \\ &\min_{k=i}^{j-1}\{f[i,k]+f[k+1,j]\}+L[j]-L[i-1] & & \text{if}\quad i<j\\ \end{aligned}\right.\)
那么最终将整个字符串划分的最小代价为\(f[1,m]\)。
1 | BREAKING-STRING-GEN-SOL(sol, prev, i, j) |
14-10
这道题的题意似乎并不太清晰,需要多添加另外\(2\)个假设:
- 每一年投资得到的利息,不能用于下一次投资。也就是说,每一年投资额恒为\(10000\)美元。
- 每年年末支付\(f_1,f_2\)时,是额外支付的,并不从投资的本金中支付。
a
假设存在一个最优的投资计划,并且第\(y_1\)到第\(y_2\)年中,该投资计划保持不变。那么假设第\(y_1\)年开始前,对第\(i\)个投资投资了\(d_i\)美元,那么到第\(y_2\)年结束时,得到的钱为\(\displaystyle{\sum_{i=1}^n\sum_{j=y_1}^{y_2} d_i r_{ij}}\)。
可以知道\(\exists 1\le p\le n,\forall 1\le i\le n\),使得\(\displaystyle{\sum_{j=y_1}^{y_2} r_{pj}\ge\sum_{j=y_1}^{y_2} r_{ij}}\)均成立。
因此,将这个投资计划的投给第\(i\)个投资产品的钱转移到投给第\(p\)个投资产品,那么在第\(y_1\)年到第\(y_2\)年得到的钱将会更多。
也就是有:
\[\sum_{i=1}^n\sum_{j=y_1}^{y_2} d_i r_{ij}\le \sum_{i=1}^n\sum_{j=y_1}^{y_2} d_i r_{pj}=d\sum_{j=y_1}^{y_2} r_{pj}\]
最终,如果存在一种最优的方案是投资多个产品,那么通过上述剪切-粘贴技术可以将这个投资方案转化成一个更优的单一投资产品方案。
b
按照题目14-10-a的结论,如果今年的投资方案是最优的,要么就是从上一年同一个投资产品最优的方案转移而来,此时仅产生少量的费用\(f_1\),要么是从投资其它产品的最优方案直接迁移所有资金到当前产品,此时产生同样的资金转移费用。因此,单一状态\(f[i,j]\)的最优解来自于这\(n\)个子问题的最优解。
c
令\(f[i,j]\)表示第\(i\)年结束时(假设第\(i+1\)年不继续,那么就不付手续费),最终得到的钱的总数(其中\(d=10000\))。
那么不难写出状态转移方程:
\(f[i,j]= \left \{\begin{aligned} &d\cdot r_{j1} & & \text{if}\quad i=1 \\ &\max\{f[i-1,j]-f_1,\max_{k=1}^n\{f[i-1,k]\}-f_2\} +d\cdot r_{ji}& & \text{if} \quad i>1\\ \end{aligned}\right.\)
由此可得到最终答案为
\[\max_{j=1}^{n} \{f[10,j]\}\]
那么可以写出如下算法INVESTMENT-STRATEGY
,注意到第4行的嵌套循环是一个二重循环,因此可以知道整个算法的时间复杂度为\(\Theta(n\cdot y)\)。
1 | INVESTMENT-STRATEGY(r, n, d = 10000, y = 10) |
d
如果添加了这个限制,那么对于当前状态\(f[i,j]\),如果一开始投资的金额超过了\(15000\)的美元,那么需要涉及到资金分拆的问题,新生成的子问题将会涉及到多个使用分拆后的资金进行投资的问题,由此子问题数量将会急剧上升,原来我们所设计的最优子结构将不会再成立。
14-11
考虑将每一个月的月终视为动态规划的一个阶段,子问题则是上一个月剩下的机器数量。
令\(f[i, j]\)表示第\(i\)个月结束后,在仓库内部剩下的机器数量为\(j\)时,所需要交付出的最少成本。
对于每个状态\(f[i, j]\),它可以从\(0 \sim j + d_i\)这些状态转移而来,因为就算不生产,为达到当前数量,上限只能会是\(j+d_i\)。
那么不难写出状态转移方程:
\(f[i,j]= \left \{\begin{aligned} &\text{cost}(j+d_i,m,c) & & \text{if}\quad i=1 \\ &\min_{k=0}^{\min\{j+d_i,D\}} \{f[i-1,k]+\text{cost}(j-k+d_i,m,c)\}& & \text{if} \quad i>1\\ \end{aligned}\right.\)
其中,\(\text{cost}(n,m,c)=\max\{n-m,0\}\cdot c\)表示这个月生产\(n\)个机器时带来的额外成本。
由此可得到最终答案为
\[\min_{j=1}^{D} \{f[n,j]\}\]
那么可以写出如下算法INVENTORY-PLANNING
,注意到第5, 6,
7行的嵌套循环,其中第\(6\)个循环的循环上限最大为\(D\),因此可以知道整个算法的时间复杂度为\(O(nD^2)\)。
1 | COST(n, m, c) |
14-12
考虑将每一个位置视作是动态规划的一个阶段。
令\(f[i, j]\)表示已经考虑签约了前\(j\)个位置的球员,已经花费了\(j\),得到的最大VORP之和。
对于每个状态\(f[i, j]\),一共有\(P+1\)个策略:分别从\(P\)个球员中选择一个;或者是在这个位置上不招球员。
那么可以写出状态转移方程:
\(f[i,j]= \left \{\begin{aligned} &0 & & \text{if}\quad i=0\land j=0 \\ &\max\{f[i-1,j],\max_{1\le k\le P,\text{cost}[i,k]\le j} \{f[i-1,j-\text{cost}[i,k]]+\text{vorp}[i,k]\}\} & & \text{if}\quad 1\le i\le n \\ &-\infty & & \text{otherwise} \\ \end{aligned}\right.\)
可以得到最终答案为
\[\max_{j=1}^{X} f[N,j]\]
那么可以写出如下算法SIGNING-FREE-AGENT-BASEBALL-PLAYERS
,注意到第4,
5, 7行的嵌套循环,循环大小分别恒为\(N,X+1,P\),因此整个程序的时间复杂度为\(\Theta(NXP)\)。
1 | // 这里同样假设X是10万的整数,并且cost[i, j], vorp[i, j]表示第i个位置第j个人的信息。 |