「学习笔记」斜率优化

太久没碰过这个玩意了,于是它就变成学习笔记了。

题目特点

一般来说,斜率优化的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)^2 \} \]

\(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\),就可以像上一道题那样写出一条直线来。

于是我们对每种颜色用单调栈维护上凸包,由于斜率单调递增,所以我们可以将队尾斜率小于当前斜率的点全部弹掉,同时取队尾为转移点即可。

posted @ 2019-10-23 17:23  xgzc  阅读(218)  评论(1编辑  收藏  举报