算法导论15.2 Exercises 答案
15.2-1
令$m$为当前满足$\dfrac{v_m}{w_m}$最大的物品编号,假设存在一个最优解其解向量$x$满足如下条件:$\exists j,1\le j\le n,j\neq m$,并且有$x_j>0\land x_m< w_j$,那么构造一个新的解向量$x’$,其中$x_m’=x_m+\min{x_j,w_m-x_m},x_j’=x_j-\min{x_j,w_m-x_m}$,那么解$x$相对解$x’$所增加的价值为
$\begin{aligned}
\Delta &= (x_m’-x_m)\cdot\dfrac{v_m}{w_m}+(x_j’-x_j)\cdot\dfrac{v_j}{w_j}\
&=\min{x_j,w_m-x_m}\cdot\left(\dfrac{v_m}{w_m}-\dfrac{v_j}{w_j}\right)\
&>0
\end{aligned}$
那么由$x’$构造出了一个更优的解$x$,因此这个问题满足贪心选择性质。
15.2-2
令状态$f[i, j]$表示前$i$个物品至多重量和为$j$时,最大的总价值。对于当前状态,第$i$个物品有两种决策:取或者是不取。如果不取,那么相当于从状态$f[i-1,j]$转移而来;如果取,那么相当于从$f[i-1,j-w_i]$处取一个物品$i$转移而来,并且从中择优转移。因此不难写出如下状态转移方程:
$f[i,j]=
\left {\begin{aligned}
&0 & & \text{if}\quad i=0 \
&f[i-1,j]& & \text{if} \quad i>0 \land j< w_i\
&\max{f[i-1,j],f[i-1,j-w_i]+v_i}& & \text{if} \quad i>0 \land j\ge w_i\
\end{aligned}\right.$
因此最终答案为$f[n,W]$。
算法0-1-KNAPSACK-PROBLEM给出了具体的实现过程。第2-3行的嵌套循环说明这个算法的时间复杂度为$O(n\cdot W)$。
1 | 0-1-KNAPSACK-PROBLEM(w, v, n, W) |
15.2-3
直接贪心选择价值最大的物品即可,算法0-1-KNAPSACK-PROBLEM'给出了具体的过程。
证明,假设一个解$S$包含了物品$(w_j,v_j)$,并且不包含物品$(w_i,v_i)(i< j)$,那么构造一组解$S’=S-{(w_j,v_j)}\cup {(w_i,v_i)}$。可以发现这个解的价值比$S$更大,因为价值增加了$v_i-v_j$,而且重量还减少了
$w_j-w_i$,这说明这个解更优,并且仍然是满足条件的。
由此,这个贪心算法是成立的。
1 | // 假设物品已经按照重量从小到大排序,此时价值是从大到小排序的。 |
15.2-4
假设整条路的情况用一个长度为$n+1$的数组$X$表示。道路上有$n$个补给点以及起点和终点,$X[1]$表示起点和下一个补给点的距离,$X[n + 1]$表示终点和最后一个补给点的距离,其余$n-1$个元素表示$n-1$对元素间的距离。
这道题使用的贪心思想是:尝试尽量到达下一个补给点(或者是终点),直到发现以当前水量不足以达下一个点了,才开始装水。
当选定好一个补给点$p$后,接下来的所有路可以视为一个同样的子问题。令子问题表示$S_i=X[i:n+1]$,那么原问题为$S_1$。
假设问题$S1$的一个最优解为选择补给点$T={x_1,x_2,x_3,\dots,x_m}$,按顺序列出,并且距离起点最远不超过$m$的补给点为$y_1$,并且有$x_1< y_1$。那么考虑证明可以构造出一个最优解,使得$y_1$是第$1$个选择的补给点。考虑如此证明:如果$x_1=y_1$,那么证明完成;否则,构造解$T’={y_1,x_2,x_3,\dots,x_m}$,可以发现$T’$和$T$的集合大小相同。一方面,由于$y_1>x_1$,从$y_1$出发可以更快的到达$x_2$,因此这个解是成立的;另一方面,子问题$S{y1}$也是$S{x1}$的一个子问题,因此$S{x1}$求出的解必定不会严格优于$S{y_1}$的解。因此整个贪心算法是成立的,具体实现由算法GEN-STOP-POINT给出。
1 | GEN-STOP-POINT(X, n, m) |
15.2-5
假设这些点$x_i$已经按照顺序排好序。那么证明:这个问题的一个最优解$S$满足:$S$中的所有区间的左端点是${x}$中的某些点。
假设解$S$中的一个区间$[lj,l_j+1]$满足$x{i-1}<l_j<x_i$,考虑区间$[x_i,x_i+1]$与其相比。可以知道,区间$[l_j,x_i)$并没有覆盖到任何点。考虑区间$(l_j+1,x_j+1]$,如果这个区间上没有点,那么区间$[x_i,x_i+1]$
和$[l_j,l_j+1]$覆盖的点是完全一样的;如果这个区间上有点,那么区间$[x_i,x_i+1]$不仅覆盖了$[l_j,l_j+1]$已经覆盖的所有点,还多覆盖了一部分点。因此无论那种情况,区间$[x_i,x_i+1]$都是比$[l_j,l_j+1]$更好的选择。故原结论成立。
并且,不难证明$S$中的所有区间两两不相交。因此我们可以给出一种算法:将最左边的点作为起点,向右延长一个单位。然后删除这个区间内的所有点,再在剩下的点中进行同样的操作。算法GEN-INTERVALS给出了详细过程。
1 | // 由于所有区间的长度都是1,因此仅返回所有区间的左端点。 |
$\star$ 15.2-6
本题的做法和问题9-3相似。假设数组$A$表示物品的列表,$A[i].w$与$A[i].v$分别表示第$i$个物品的重量和价值,那么按照值$\dfrac{A[i].v}{A[i].w}$作为关键字,将$A[i].w$作为权值,对数组$A$进行划分。最终的划分结果得出了一个下标$p$,在$p$右边的物品可以全部取完,物品$p$则按重量取完,在$p$左边的物品不取。
因此,算法FRACTION-KNAPSACK-PROBLEM2的行为和问题9-3-c的算法MEDIAN-WEIGHT一致,它们都是以$O(n)$的时间复杂度用来求解一个划分。
1 | // 判断物品q的单位价值是否比p大。 |
15.2-7
算法CAL-PAYOFF给出了整个过程,基本思想是将两个数组排序,然后直接计算值$\displaystyle{M=\prod_{i=1}^n a_i^{b_i}}$并返回即可。
正确性说明:由于此时求的$M$值是乘积,那么对$M$求对数后,相当于是求$\displaystyle{S=\lg M=\sum_{i=1}^n b_i\lg a_i}$的最大值。由于$a_i$是正数,因此$\lg a_i$是非负的,令$a_i’=\lg a_i$。
假设一个解$A$中,$a_i’$和$b_i$配对,$a_j’$和$b_j$配对,并且有$a_i’<a_j’,b_i>b_j$,那么此时这两对数对$A$的贡献为$a_i’b_i+a_j’b_j$。考虑将这两对数中的$b$交换,得到一个新解$A’$,此时这两队数的贡献为$a_i’b_j+a_j’b_i$。
可以发现,解$A’$相比解$A$的花费变化为$a_i’b_j+a_j’b_i-a_i’b_i-a_j’b_j=(a’_j-a_i’)(b_i-b_j)>0$。这说明解$A’$比解$A$更优。因此,这种贪心方法是正确的。
1 | // 假设物品已经按照重量从小到大排序,此时价值是从大到小排序的。 |