太久没碰过这个玩意了,于是它就变成学习笔记了。
题目特点
一般来说,斜率优化的dp会比其它的dp得到转移方程要简单一点点。
通式大概是:
\[
f[i] = a(i)b(j) + c(i) + d(j)
\]
也就是说,这种dp和单调队列不同的一点是有同时和\(i,j\)有关的项,这时候就需要用到斜率优化。
拿道题目来讲可能效果更好一些。
P3195 [HNOI2008]玩具装箱
设前缀和为\(s(i)\),那么很快得到dp方程:
\[
f[i] = \min_{j < i}\{f[j] + (s(i) + i - s(j) - j - L - 1) \}
\]
设\(a(i) = s(i) + i, b(i) = a(i) + L + 1\)。
于是
\[
\begin{aligned}f[i] &= f[j] + (a(i) - b(j))^2 \\&= f[j] + a(i)^2 - 2a(i)b(j) + b(j)^2\end{aligned}
\]
移项可得
\[
2 a(i)b(j) + f[i] - a(i)^2 = f[j] + b(j)^2
\]
令\(b(j) = x, f[j]+b(j)^2 = y\),那么这个方程就可以看成一条平面直角坐标系上的一条直线。
此时\(f[i]\)的含义变为,当这条直线经过点\(P(x, y)\)时,与\(y\)轴的截距加\(a(i)^2\)的值(\(a(i)^2\)是一个定值)。
于是我们只需要找到这个斜率的最小值。
我们可以画一个图辅助理解。(图源oi-wiki,侵删)
那么我们可以用一个单调队列维护下凸包就可以了。
由于在这道题中,斜率是单调递增的,所以我们可以将队头斜率小于当前斜率的点全部弹掉,取队头为转移点即可。
P5504 [JSOI2011]柠檬
首先需要知道一个性质:每一段左右两端的贝壳大小相同,而且这一段的\(s_0\)即为左右两端贝壳的大小。
那么考虑dp,设\(f[i]\)表示前\(i\)个数能够获得的最多柠檬数,\(c_i\)表示这种大小第几次出现。
那么有
\[
f[i] = \max_{j \leq i, s_i = s_j}\{f[j - 1] + s_i(c_i - c_j + 1)^2 \}
\]
把所有项拆开有:
\[
f[i] = f[j - 1] + s_ic_i^2 - 2s_ic_ic_j+s_ic_j^2+2s_ic_i-2s_ic_j+s_i
\]
移项可得:
\[
f[j - 1] - 2s_ic_j + s_ic_j^2 = f[i] - 2s_ic_i - s_ic_i^2 + s_i + 2s_ic_ic_j
\]
令\(c_j = x, \ \ f[j - 1] - 2s_ic_j + s_ic_j^2 = y\),就可以像上一道题那样写出一条直线来。
于是我们对每种颜色用单调队列维护上凸包,由于斜率单调递增,所以我们可以将队尾斜率小于当前斜率的点全部弹掉,同时取队尾为转移点即可。