斜率优化dp

博客介绍了斜率优化算法,适用于形如dp[i]=min{A(i)×B(j)+C(i)+D(j)}的转移方程。介绍了线性规划和代数法两种方法,还按xj和ki的单调性分类阐述实现方式,同时给出实现细节,如处理xj非严格递增、手写队列初始化等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

算法介绍

使用条件

形如dp[i]=min{A(i)×B(j)+C(i)+D(j)}dp[i]=min\{A(i)\times B(j)+C(i)+D(j)\}dp[i]=min{A(i)×B(j)+C(i)+D(j)}的转移方程可尝试使用斜率优化。
(取maxmaxmax是类似的,本文不赘述)

法一:线性规划

考虑把 dp[i]=A(i)×B(j)+C(i)+D(j)dp[i]=A(i)\times B(j)+C(i)+D(j)dp[i]=A(i)×B(j)+C(i)+D(j) 化成y=kx+by=kx+by=kx+b的形式,其中y,xy,xy,x只与jjj有关,k,bk,bk,b只与iii有关,且 xxx严格单调递增

则有:
D(j)=−A(i)×B(j)+dp[i]−C(i)D(j)=-A(i)\times B(j)+dp[i]-C(i)D(j)=A(i)×B(j)+dp[i]C(i)
其中y=D(j),k=−A(i),x=B(j),b=dp[i]−C(i)y=D(j),k=-A(i),x=B(j),b=dp[i]-C(i)y=D(j),k=A(i),x=B(j),b=dp[i]C(i)

在平面直角坐标系上把所有的(xj,yj)(x_j,y_j)(xj,yj)(即(B(j),D(j))(B(j),D(j))(B(j),D(j)))标出来。
要令dp[i]dp[i]dp[i]最小,则要令bbb最小,即是要找到某一点(xj,yj)(x_j,y_j)(xj,yj),使斜率为kkk的直线经过该点时算出来的bbb最小。
发现只有 在给出点的下凸壳上的点可能成为最优决策点。
在这里插入图片描述

法二:代数法(数形结合)

j1,j2j_1,j_2j1,j2 (0≤j1<j2<i)(0\leq j_1<j_2<i)(0j1<j2<i)dp[i]dp[i]dp[i]的两个决策点,且决策点j2j_2j2优于j1j_1j1,则有:
A(i)×B(j2)+C(i)+D(j2)≤A(i)×B(j1)+C(i)+D(j1)A(i)\times B(j_2)+C(i)+D(j_2)\leq A(i)\times B(j_1)+C(i)+D(j_1)A(i)×B(j2)+C(i)+D(j2)A(i)×B(j1)+C(i)+D(j1)

化简式子,使不等号左边只与iii有关,不等号右边只与j1,j2j_1,j_2j1,j2有关:
A(i)≤−D(j2)−D(j1)B(j2)−B(j1)A(i)\leq -\frac{D(j_2)-D(j_1)}{B(j_2)-B(j_1)}A(i)B(j2)B(j1)D(j2)D(j1)
−A(i)≥D(j2)−D(j1)B(j2)−B(j1)-A(i)\geq \frac{D(j_2)-D(j_1)}{B(j_2)-B(j_1)}A(i)B(j2)B(j1)D(j2)D(j1)
xj=B(j),yj=D(j)x_j=B(j),y_j=D(j)xj=B(j),yj=D(j),则D(j2)−D(j1)B(j2)−B(j1)\frac{D(j_2)-D(j_1)}{B(j_2)-B(j_1)}B(j2)B(j1)D(j2)D(j1)可以看作Pj1(xj1,yj1)P_{j_1}(x_{j_1},y_{j_1})Pj1(xj1,yj1),Pj2(xj2,yj2)P_{j_2}(x_{j_2},y_{j_2})Pj2(xj2,yj2)两点连线的斜率。

也就是说,如果存在两个决策点j1,j2j_1,j_2j1,j2满足0≤j1<j2<i0\leq j_1<j_2<i0j1<j2<i,使得Pj1P_{j_1}Pj1,Pj2P_{j_2}Pj2两点连线的斜率≤−A(i)\leq -A(i)A(i),那么决策点j2j_2j2优于j1j_1j1

在这里插入图片描述
于是,对于这样子的三个点,可证BBB一定不是最优决策点

我们可以将BBB从候选决策点中踢出去(删除),只留下AAACCC,删后的情况如下图所示:
在这里插入图片描述
最终,我们维护的候选决策点应该构成了一个下凸壳:
在这里插入图片描述

实现

xjx_jxj的单调性分类

  1. xjx_jxj递增(递减的话给xxx取个负就是递增了):单调队列维护凸包
  2. xjx_jxj不单调:平衡树维护凸包/cdq分治提供单调性

kik_iki的单调性分类

  1. kik_iki递增: 只需要维护部分凸包。即用单调队列维护凸包时可以不断弹掉队首,删掉不需要维护的凸包;查询答案时直接取队首即可。
  2. kik_iki不递增:必须维护完整凸包。即用单调队列维护凸包时不能再弹掉队首了(即用单调栈维护凸包);查询答案时二分找最优决策点。

实现细节

  1. xjx_jxj 非严格递增时,在求斜率时可能会出现 xj1=xj2x_{j1}=x_{j2}xj1=xj2 的情况,此时最好写成这样的形式:return Y(j)>=Y(i)?inf:-inf,而不要直接返回 infinfinf 或者 −inf−infinf
  2. 手写队列的初始化是 h=1,t=0,由于队列初始化大多都要塞入一个点 P(0)P(0)P(0),所以在一些题解中可以看到h=t=1
  3. 手写队列判断不为空的条件是h<=t,而出入队判断都需要有至少 2 两个元素才能进行操作。所以应是 h<t
  4. 计算斜率可能会因为向下取整而出现误差,所以 slopeslopeslope 函数最好设为 longdoublelong doublelongdouble 类型。
  5. 在比较两个斜率时,尽量写上等于,即<=>= 而不是<>。这样写对于去重有奇效(有重点时会导致斜率分母出锅),但不要以为这样就可以完全去重,因为要考虑的情况可能会非常复杂,所以还是推荐加上 1. 中提到的特判,确保万无一失。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值