学习斜率优化发现要用到凸包,一查发现国外管斜率优化叫凸包优化
**具体怎么求凸包?**
以最常见的求下凸壳 为例(对应DP取min的情况),假设我们的决策点 x 坐标是单调递增的。
**核心思想:维护一个单调队列,保证队列中相邻两点连线的斜率是单调递增的。**
想象你在从左到右扫描这些点,手里拿着一根线,要把它绷紧在所有点的下方:
-
准备阶段 :把所有决策点按
x坐标从小到大排好序(如果DP过程本身就让x单调递增,那就不用额外排序了)。 -
逐个加入点 :用一个栈或队列来维护凸包上的点。每加入一个新点
C,就检查它和栈顶两个点A、B的关系。 -
判断凹点并剔除 :计算
AB连线的斜率k1和BC连线的斜率k2。- 如果
k1 < k2,说明B是凸点,保留它,把C入栈。 - 如果
k1 >= k2,说明B是凹点(或者共线),它不可能成为最优决策点,把B弹出栈,然后继续用新的栈顶两个点和C比较,直到满足k1 < k2为止。
- 如果
-
重复:处理完所有点后,栈里剩下的点就是下凸壳上的点,按顺序排列。
用叉积来判断,避免除法精度问题
实际写代码时,我们一般不用真的算斜率(除法会有精度问题),而是用叉积来判断三个点的凹凸关系。
对于点 A(x1, y1)、B(x2, y2)、C(x3, y3),计算向量 AB 和 BC 的叉积:
cross = (x2 - x1) * (y3 - y2) - (y2 - y1) * (x3 - x2)
- 如果
cross > 0,说明B是凸点(下凸壳保留)。 - 如果
cross <= 0,说明B是凹点或共线,应该弹出。
这就是Andrew算法求凸包的核心逻辑,在斜率优化DP里我们通常只维护下半部分凸壳。