太久没碰过这个玩意了,于是它就变成学习笔记了。
题目特点
一般来说,斜率优化的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)。
于是
[egin{aligned}f[i] &= f[j] + (a(i) - b(j))^2 \&= f[j] + a(i)^2 - 2a(i)b(j) + b(j)^2end{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),就可以像上一道题那样写出一条直线来。
于是我们对每种颜色用单调栈维护上凸包,由于斜率单调递增,所以我们可以将队尾斜率小于当前斜率的点全部弹掉,同时取队尾为转移点即可。