算法导论17 Problems 答案
17-1
a
令$z$是当前被所有区间覆盖得最多次数的一个点。我们可以将$z$稍微向右移动,只要不接触到某条线段的端点,那么覆盖到$p$的线段次数就不会改变;只要移出了某条线段的右端点,它的覆盖次数才会被改变。因此,我们只需要将点$p$移动到覆盖到它的所有线段的最小右端点$p’$,它仍然是其中一个最优解。因此,题目所求的最大覆盖点中,某条线段的右端点一定是一个最优解。
b
这道题的静态做法是,将每条线段的每个端点取出来进行排序,左端点对应值$+1$,右端点对应值$-1$,并且如果端点值相同,那么$+1$的端点应该在$-1$的端点的左边,对于端点$p$,它的属性$t$表示它的类型,$x$表示它的坐标。假设排好序后,其顺序$p1,p_2,\dots,p{2n}$,那么我们需要求的是$\displaystyle{\max{i=1}^{2n}\left{\sum{j=1}^i p_j.t\right}}$,这是一个非常标准的前缀和问题。
考虑使用这个思路将这个静态的做法转成动态的。如果往数据结构插入一个区间,那么就相当于向红黑树插入了两个节点(表示左端点和右端点的坐标)。每个节点$x$有三个属性$t,s,m$,它们分别表示节点$x$的权值(要么为$-1$,要么为$1$);以节点$x$为根的子树权值$t$之和;以节点$x$为根的子树中,$t$的中序的前缀权值最大和,此外还可以添加一个属性$p$,表示以$x$为根的子树中,达到$t$的中序前缀最大和$x.m$的节点。我们可以写出$s,m$的关系:
- $x.s=x.left.s+x.t+x.right.s$
- $x.m=\max{x.left.m,x.left.s+x.t,x.left.s+x.t+x.right.m}$
在旋转的过程中,只需要维护好属性$s,m$的值即可。对于空节点$T.nil$,其$t,s,m$属性值均为$0$,$p$属性值指向自身,即$T.nil$。
1 | RB-UPDATE-M-AND-S(z) |
上面的代码中,$T$是一棵支持属性$s,m,p,a$的树。按照如上改动之后,FIND-POM方法可以在$O(1)$的时间内返回最大覆盖点$T.root.p$,插入操作INTERVAL-INSERT-OVERLAP/删除操作INTERVAL-DELETE-OVERLAP则是调用了两次红黑树内部的插入/删除操作,其时间复杂度为$O(n\lg n)$。对于子程序RB-INSERT-M-AND-S和内部调用的RB-INSERT-FIXUP-M-AND-S,以及子程序RB-DELETE-M-AND-S和内部调用的RB-DELETE-FIXUP-M-AND-S,其形式和题目17.3-5基本相同,区别在于题目17.3-5所调用的更新节点值子程序是RB-UPDATE-MINGAP,而这里的更新节点值子程序是RB-UPDATE-M-AND-S。
17-2
a
先初始化一个双向链表$L$,首尾相接。在第$i$轮中,迭代链表$m\bmod(n-i+1)$次,找到这个节点后记录号码并且删除即可。如果这是一个双向链表,还可以考虑通过比较$m\bmod(n-i+1)$和$(n-i+1)-m\bmod (n-i+1)$的大小,从而决定正向迭代还是反向迭代。
这种暴力的算法的时间复杂度为$O(nm)$,由于$m$是一个常数,因此其时间复杂度可以简化为$O(n)$。
b
如果$m$是不定值,那么先将从$1$到$n$这$n$个数插入到红黑树中。假设节点$x$被删除之前,其排名值为$r$,那么删除后,接下来就是删除排名为$r+m_i-1\bmod (n-i+1)+1$的节点。由于$n$轮迭代中,每轮搜索了一次节点和和删除一次节点,它们都是$O(n\lg n)$的操作,因此其总体时间复杂度为$O(n\lg n)$。这个算法由程序JOSEPHUS-UNCONSTANT-M给出。
1 | JOSEPHUS-UNCONSTANT-M(M, n) |