关于斜率优化的一些杂谈

这里并不是在详细地介绍斜率优化,只是一些瞎扯,想真正系统学习斜率优化的话请去阅读其他文章。

斜率优化是众多 dp 优化方式中较为常见的一种,让我们不妨回忆一下它的形式:

dp(i)=min/max(a(i)×b(j)+c(i)+d(j)+C)

上式中,a,b,c,d 分别为只跟 ij 相关的函数,C 是一个与 ij 都无关的常量。

对于此类转移,传统的优化方式通常是将每个决策点看作二维平面上的一个点,维护一个上/下凸壳,将每次转移看作是一条直线去切这个凸壳,求出第一个切点并转移。

而维护的方式也需要根据题目的不同来改变,比如:

  • 当每次转移对应的直线斜率 k 和每次加入点的横坐标 xi 都是单调递增的时,我们可以直接使用一个单调队列维护凸壳,每次转移直接踢掉不优的决策点后取出队头即可。
  • 当每次转移对应的直线斜率 k 不再单调,但是每次加入点的横坐标 xi 还是单调的时,我们还是可以使用单调队列维护凸壳,但是转移时我们需要通过二分找出最优决策点。
  • 如果两者都不单调,那么我们需要使用平衡树或 CDQ 分治的方式来维护凸壳,每次转移也需要二分来寻找切点。

这么多种情况,使得我们在实现的时候需要仔细考虑凸壳的维护方式,并且维护凸壳过程的本身就需要很多的讨论与细节,而且凸壳的具体形状也是我们必须要考虑的。

事实上,我们有一个更普遍的做法——使用李超树,不再去维护凸包,转而将每个决策点看作一个一次函数。

具体地,回到上面的式子,我们将只和 i 相关的项和常量分离出来,写成下面这样:

dp(i)=min/max(b(j)×a(i)+d(j))+c(i)+C

发现了没有?每个决策点 j 都可以看作是一条斜率为 b(j),截距为 d(j) 的一次函数,转移相当于是求多个一次函数在某点上的最值。

使用李超树的优势还是很多的,不需要动脑子考虑那么多的东西,实现起来比较简单粗暴,也少了很多繁杂的推式子的过程。而且对于斜率优化问题,我们使用李超树一般直接维护直线即可,因此复杂度是一个 log 的,代码难度和细节比平衡树和 CDQ 分治大概也要低很多。综上所述,对于斜率优化问题直接无脑上李超树维护也是一个很好的选择。

于是乎我直接找了两道之前见过的斜率优化题尝试了一下李超树写法。

洛谷 P3195 [HNOI2008] 玩具装箱

题目链接

洛谷 P4655 [CEOI2017] Building Bridges

题目链接

posted @   KingPowers  阅读(33)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
点击右上角即可分享
微信分享提示