斜率优化

斜率优化可以在动态规划中根据斜率的性质优化时间。

当转移出现ai×bja_i\times b_j的时候,单调队列优化不再适用,就要使用斜率优化。

例题

[HNOI2008]玩具装箱
给定nn个玩具,每个玩具价值CiC_i,分成若干段,定义分段代价cost(i,j)=(ji+k=ijCjL)2,ijcost(i,j)=(j-i+\sum_{k=i}^jC_j -L)^2,i\leq j,LL为给定常数,求分段的最小代价和。
1n5×104,1L,Ci1071\leq n \leq 5\times 10^4,1\leq L,C_i\leq 10^7


朴素做法

fif_i表示前ii个物品分段的最小代价,preipre_i为价值前缀和。

fi=minj<i{fj+(ij1+preiprejL)2}f_i=\min_{j<i}\{f_j+(i-j-1+pre_i-pre_j-L)^2\}

时间复杂度O(n2)O(n^2),无法通过本题。

优化

si=i+preis_i=i+pre_i,L=L+1L=L+1

fi=minj<i{fj+(sisjL)2}=minj<i{fj+(siL)2+2(Lsi)sj+sj2}f_i=\min_{j<i}\{f_j+(s_i-s_j-L)^2\}=\min_{j<i}\{f_j+(s_i-L)^2+2(L-s_i)s_j+s_j^2\}

fi(siL)2=minj<i{fj+sj2+2sj(Lsi)}f_i-(s_i-L)^2=\min_{j<i}\{f_j+s_j^2+2s_j(L-s_i)\}

fi(siL)2f_i-(s_i-L)^2看作bib_ifj+sj2f_j+s_j^2看作yjy_jsjs_j看作xjx_j2(Lsi)-2(L-s_i)看作kik_i,就有

bi=minj<i{yjkixj}b_i=\min_{j<i}\{y_j-k_ix_j\}

(xj,yj)(x_j,y_j)看作坐标系的点,于是转换成定斜率过一点求最小截距

几个性质:

  1. 最优决策点在下凸壳,可以直接维护凸包。
  2. 本题的斜率单调递增。
  3. 决策单调性:cost(i,j)=(sisjL)2cost(i,j)=(s_i-s_j-L)^2是四边形不等式(同时斜率单增也可证明)。

所以可以用单调队列维护一个凸包,具体做法如下:

  1. 弹队首,条件为队首代表线段斜率小于 kik_i,此时选下一个点肯定更优。

  2. 队首为最优决策点,更新(决策单调性)。

  3. 加入(xi,yi)(x_i,y_i)决策点,出队当且仅当新的凸包不包含队尾节点(比较斜率)。

  4. 加入当前决策点。

复杂度O(n)O(n),完美解决。

code on Luogu

进阶

如果斜率不单调怎么办?即决策也不单调。

但这仍然满足最优决策点在凸包上,且新增决策点x单增,我们仍然维护凸包上的点,对于给定斜率kk,最优决策点一定是在凸包上的直线刚恰好切上凸包的点,满足单调性,故可以用二分答案去找最优决策点。

[SDOI2012]任务安排
nn个任务分段并顺序完成,给定完成时间TT和费用系数CC,完成一段前需冷却ss,完成时间为这段时间之和,每个任务的价值为完成时间×\times费用系数,最小化价值和。
1n3×105,1s28,Ti28,0Ci281 \leq n \leq 3 \times 10^5,1\leq s \leq 2^8,|T_i|\leq 2^8,0 \leq C_i\leq 2^8


先看O(n2)O(n^2)的做法,fif_i表示前ii个任务的最小价值和。

最朴素做法中需要一维分了几组的状态,本题不要求分组数量,故有所多余。

这里有个trick:转移的时候,根据乘法分配率,对[j,i][j,i]分组,ii以后的任务都多了一个s×Cis\times C_i的价值,可以直接加上去,于是转移就分为准备时间的贡献做任务的贡献,这时转移的时候就不必计算当前批任务的具体完成时间,改为计算不计冷却的完成时间。

设时间前缀ptipt_i,价值前缀pcipc_i

fi=minj<i{fj+pti×(pcipcj)+s×(pcnpci)}f_i=\min_{j < i}\{f_j+pt_i\times(pc_i-pc_j)+s\times(pc_n-pc_i)\}

优化

fi=minj<i{fj+ptipciptipcj+pcnspcis}f_i=\min_{j<i}\{f_j+pt_ipc_i-pt_ipc_j+pc_ns-pc_is\}

fiptipcipcns+pcis=minj<i{fjptipcj}f_i-pt_ipc_i-pc_ns+pc_is=\min_{j<i}\{f_j-pt_ipc_j\}

bi=fiptipcipcns+pcisb_i=f_i-pt_ipc_i-pc_ns+pc_is

yj=fj,xj=pcj,ki=ptiy_j=f_j,x_j=pc_j,k_i=pt_i

bi=minj<i{yjkixj}b_i=\min_{j<i}\{y_j-k_ix_j\}

这里由于TiT_i可负,斜率不再单调,但xx仍然单调,利用上文做法,运用二分答案找最优决策点。

复杂度O(nlogn)O(nlogn)

再进阶

如果xx也不单调怎么办,维护凸包就不能用队列维护,但我们有平衡树!但是实现比较繁琐。(代码能力太弱)

玩具装箱题目为例子,这里Ci107|C_i|\leq 10^7

我们发现只需要使x,kx,k单调就能用基础做法解决问题,可以使用创造单调性的利器CDQ分治创造单调性,具体的:例如分治[l,r][l,r],加入决策点[l,mid][l,mid],更新状态[mid+1,r][mid+1,r],右侧没有考虑到的决策点会在向右递归的时候处理到,且[l,mid][l,mid]的状态的答案一定已经处理完全,所以正确性得证,复杂度O(nlog2n)O(nlog^2n),多出的loglog在于左区间xx的排序和右区间kk的排序。

练习题

鸽子待出

参考资料

Author: cjh-hhz
Link: https://cjh-hhz.github.io/ec98c4a655e5.html/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.