对于两个点集 $A$ 与 $B$,它们的闵可夫斯基和就是 $A + B = {\mathbf a + \mathbf b \rvert \mathbf a \in A, \mathbf b \in B}$。本文只讨论 $A$ 和 $B$ 都是凸包的情况。
符号和约定
$|A|$ 表示凸包 $A$ 的顶点个数,$A_i$ 表示凸包的第 $i$ 个顶点,并且下标是循环的(即 $A_i$ 与 $A_{i+|A|}$ 指同一顶点)。
结论和实现
将 $\vec{A_iA_{i+1}}(i \in [1, |A|])$ 和 $\vec{B_iB_{i+1}}(i \in [1, |B|])$ 这 $|A| + |B|$ 条向量极角排序,并合并其中的同向边,顺次连接,即可得到 $A+B$ 的轮廓。
实现上,我们不会采用比较斜率的方式来极角排序,因为那样涉及浮点运算;我们可以用向量的叉积作为排序的比较器,实现 $O(n\log n)$ 的算法。进一步地,由于 $\vec{A_iA_{i+1}}(i \in [1, |A|])$ 和 $\vec{B_iB_{i+1}}(i \in [1, |B|])$ 各自原本就是按极角有序的,我们还可以使用类似归并排序的方式 $O(n)$ 地合并它们。
凸包的闵可夫斯基和在算法竞赛中的应用
利用凸包求和,我们可以快速地进行最值卷积,即以 $O(|F|+|G|)$ 的时间复杂度求出 $\forall k, P_k = \max\limits_{i+j=k} F_i + G_j$ 和 $\forall k, Q_k = \min\limits_{i+j=k} F_i + G_j$;配合分治式合并可以在 $O(n\log n)$ 的时间内完成一系列大小总和不超过 $n$ 的数列的合并。Splay 启发式合并也可以做到 $O(n\log n)$ 的复杂度,并且相比分治式合并维护树上 DP $O(n^2)$ 的复杂度(这和暴力复杂度相同),它却可以 $O(n\log n)$ 维护树上 DP(因为 Splay 启发式合并的复杂度事实上是 $O((n-a)\log n)$,其中 $a$ 为最大数组的大小)。方便起见,我们一般只维护凸包的上轮廓或是下轮廓。
最值卷积听起来很让人激动。类似“给定若干个特定重量和价值的物品,求总重量为特定值时物品的最大总价值”一类的背包问题难道就这样轻松解决了吗?我们把每单个物品都视作一条线段(在我们的理论中,线段也属于凸包),对这些凸包进行合并,不就得到最终答案对应的凸包了吗?事实并非如此美好。因为最终求到的结果中,某些答案对应的点不在凸包的顶点上,而在凸包的边上;而由于单个物品只能取到线段的两个端点而不能取到线段上的非端点(即物品只可取 0 个或 1 个而不能取 0.5 个),最终我们得到的凸包上的非顶点也是不能取的。也就是说,这样求出的凸包,只有顶点对应了的重量求出的是正确答案,其余均求得偏大了。换句话讲,闵可夫斯基和本质上求的是所有可行解的凸包,这些可行解有的在凸包内部,有的在凸包顶点上;只有那些恰好在顶点上的最优解被得到了,而那些不在顶点上的最优解,我们只知道其在这个凸包的范围内,而不知其具体值。
为了保证我们能得到所有最优解,必须确保最优解具有凸性,从而它们都在凸包顶点上。最优解是否具有凸性,是能否使用闵可夫斯基和的重要依据。