斜率优化可以在动态规划
中根据斜率的性质优化时间。
当转移出现ai×bj的时候,单调队列优化不再适用,就要使用斜率优化。
例题
[HNOI2008]玩具装箱
给定n个玩具,每个玩具价值Ci,分成若干段,定义分段代价cost(i,j)=(j−i+∑k=ijCj−L)2,i≤j,L为给定常数,求分段的最小代价和。
1≤n≤5×104,1≤L,Ci≤107
朴素做法
fi表示前i个物品分段的最小代价,prei为价值前缀和。
fi=j<imin{fj+(i−j−1+prei−prej−L)2}
时间复杂度O(n2),无法通过本题。
优化
设si=i+prei,L=L+1
fi=j<imin{fj+(si−sj−L)2}=j<imin{fj+(si−L)2+2(L−si)sj+sj2}
fi−(si−L)2=j<imin{fj+sj2+2sj(L−si)}
将fi−(si−L)2看作bi,fj+sj2看作yj,sj看作xj,−2(L−si)看作ki,就有
bi=j<imin{yj−kixj}
将(xj,yj)看作坐标系的点,于是转换成定斜率过一点求最小截距。
几个性质:
- 最优决策点在下凸壳,可以直接维护凸包。
- 本题的斜率单调递增。
- 决策单调性:cost(i,j)=(si−sj−L)2是四边形不等式(同时斜率单增也可证明)。
所以可以用单调队列维护一个凸包,具体做法如下:
-
弹队首,条件为队首代表线段斜率小于 ki,此时选下一个点肯定更优。
-
队首为最优决策点,更新(决策单调性)。
-
加入(xi,yi)决策点,出队当且仅当新的凸包不包含队尾节点(比较斜率)。
-
加入当前决策点。
复杂度O(n),完美解决。
code on Luogu
进阶
如果斜率不单调怎么办?即决策也不单调。
但这仍然满足最优决策点在凸包上
,且新增决策点x单增
,我们仍然维护凸包上的点,对于给定斜率k,最优决策点一定是在凸包上的直线刚恰好切上凸包的点,满足单调性,故可以用二分答案去找最优决策点。
[SDOI2012]任务安排
将n个任务分段并顺序完成,给定完成时间T和费用系数C,完成一段前需冷却s,完成时间为这段时间之和,每个任务的价值为完成时间×费用系数,最小化价值和。
1≤n≤3×105,1≤s≤28,∣Ti∣≤28,0≤Ci≤28
先看O(n2)的做法,fi表示前i个任务的最小价值和。
最朴素做法中需要一维分了几组
的状态,本题不要求分组数量,故有所多余。
这里有个trick:转移的时候,根据乘法分配率,对[j,i]分组,i以后的任务都多了一个s×Ci的价值,可以直接加上去,于是转移就分为准备时间的贡献
和做任务的贡献
,这时转移的时候就不必计算当前批任务的具体完成时间,改为计算不计冷却的完成时间。
设时间前缀pti,价值前缀pci
fi=j<imin{fj+pti×(pci−pcj)+s×(pcn−pci)}
优化
fi=j<imin{fj+ptipci−ptipcj+pcns−pcis}
fi−ptipci−pcns+pcis=j<imin{fj−ptipcj}
bi=fi−ptipci−pcns+pcis
yj=fj,xj=pcj,ki=pti
bi=j<imin{yj−kixj}
这里由于Ti可负,斜率不再单调,但x仍然单调,利用上文做法,运用二分答案找最优决策点。
复杂度O(nlogn)
再进阶
如果x也不单调怎么办,维护凸包就不能用队列维护,但我们有平衡树
!但是实现比较繁琐。(代码能力太弱)
以玩具装箱
题目为例子,这里∣Ci∣≤107。
我们发现只需要使x,k单调就能用基础做法解决问题,可以使用创造单调性的利器CDQ分治
创造单调性,具体的:例如分治[l,r],加入决策点[l,mid],更新状态[mid+1,r],右侧没有考虑到的决策点会在向右递归的时候处理到,且[l,mid]的状态的答案一定已经处理完全,所以正确性得证,复杂度O(nlog2n),多出的log在于左区间x的排序和右区间k的排序。
练习题
鸽子待出
参考资料