斜率优化学习笔记
斜率优化学习笔记
写在前面
很早以前写的了,看着当时写的很认真,所以发到自己的博客上去。
〇、前言
本来最近一段时间是想搞网络流的。结果最近几天老师要讲斜率优化,加上之前想学但是没看懂,就有了这篇学习笔记。
感谢 这篇博客 让我看懂了斜率优化。
一、基本概念
目前我所了解的,斜率优化是用来优化 $dp$,形如:
$$
f_i=\min / \max{f_j+A_i\times B_j+C_j+D_i}
$$
看起来与单调队列的:
$$
f_i=\min/\max{f_j+A_i+B_j}
$$
有点类似,因为斜率优化正是利用单调队列来实现的。
不过这不重要。重要的是理解思想。
二、算法介绍
首先看一道经典例题:
P3195 [HNOI2008]玩具装箱
设 $f_i$ 为以 $i$ 结尾的最小总费用。不难想到 $n^2$ 的 $dp$ 转移方程,如果想不到的话建议巩固 $dp$ 基础:
$$
f_i=\min\limits_{0\le j<i}{f_j+(sum_i+i-sum_j-j-L-1)^2}
$$
将与 $i$ 相关的放在一起,与 $j$ 相关和常数项的放在一起。事实上,常数项也可以和 $i$ 相关的放在一起。
令 $A=sum_i+i,B=sum_j+j+L+1$,则原式化简为:
$$
f_i=\min\limits_{0\le j<i}{f_j+(A-B)^2}=\min\limits_{0\le j<i}{f_j+A^2-2AB+B^2}
$$
为了方便,把 $\min$ 去了,然后把与只 $j$ 相关的项扔到等式右边,其他在左边:
$$
2A\times B+f_i-A^2=f_j+B^2
$$
$$
\begin{matrix}\underbrace{2A}\k\end{matrix}\times \begin{matrix}\underbrace{B}\x\end{matrix}+\begin{matrix}\underbrace{f_i-A^2}\b\end{matrix}=\begin{matrix}\underbrace{f_j+B^2}\y\end{matrix}
$$
问题转化成了:我们可以将经过许多点 $P_j(B_j,f_j+B_j^2)$ ,斜率为 $2A$ 的直线,所得的最小截距$(f_i-A^2)$的点为最佳转移点。
如何求这最佳转移点呢,还是要对于每次转移,$O(n)$ 的时间扫一遍吗?
我们先看一个问题,如下图,对于经过两个点 $N(a_1,b_1),M(a_2,b_2)$ ,$a_1<a_2$,斜率都为 $k$ 的直线,什么时候经过 $N$ 的截距小呢?什么时候经过 $M$ 点的截距小呢?
考虑先写出直线方程 :
对于 $N$点 ,$y-b_1=k(x-a_1)$ ,化简得 截距$B_1=b_1-k\cdot a_1$
对于 $M$点,$y-b_2=k(x-a_2)$ ,化简得 截距$B_2=b_2-k\cdot a_2$
如果 $N$ 点截距小,则 $B_1<B_2$ 解得 $k<\dfrac{b_1-b_2}{a_1-a_2}$
如果 $M$ 点截距小,则 $B_1>B_2$ 解得 $k>\dfrac{b_1-b_2}{a_1-a_2}$
回到原题,我们发现 $A=sum_i+i$ 是单调递增的。也就是说,斜率 $k$ 随着转移点 $i$ 的增大而增大。
对于斜率 $k$ 不单调递增的情况下,我们后文会接着讨论。
对于两个点 $P_j(a_j,b_j)$ 和 $P_k(a_k,b_k)$ ,$a_j<a_k$ ,如果此时 $k<\dfrac{b_j-b_k}{a_j-a_k}$ ,那么转移 $j$ 点,反之转移 $k$ 点,而且之后再也不会转移到 $j$ 点。类似一个弹出操作。你可能会想到用一个数据结构维护这个弹出操作,是什么呢?
正是我们前文讲到的单调队列!
巧的是,每个转换而来的点 $P_j(B_j,f_j+B_j^2)$ ,其中 $B_j$ 也都是递增的,这表明着可以将点 $P_j$ 插入队尾而不需要插入中间的某一位置。
对于 $B_j$ 不单调递增的情况下,我们后文会接着讨论。而且现在我也没有遇到过。
现在,已经转移好了 $i$ 点,可是如何将 $i$ 点从队尾加入了这单调队列,转移其他的点呢?
如下图,假设我们现在又有三个点,分别是 $P_1(a_1,b_1),P_2(a_2,b_2)$ 和我们将要加入的点 $P_3(a_3,b_3)$,其中 $a_1<a_2<a_3$。
为了方便叙述,我们定义 $k_1=\dfrac{b_1-b_2}{a_1-a_2},k_2=\dfrac{b_2-b_3}{a_2-a_3},k_3=\dfrac{b_1-b_3}{a_1-a_3}$。
- 对于 $k_1<k_2$ 的情况
对于斜率 $k$:
如果使 $P_1$ 截距最小,$k<k_1$
如果使 $P_2$ 截距最小,$k_1\le k<k_2$
如果使 $P_3$ 截距最小,$k2\le k$
- 对于 $k_1>k_2$ 的情况
对于斜率 $k$:
如果使 $P_1$ 截距最小,$k<k_3$
如果使 $P_2$ 截距最小,无解,所以无论如何,最小截距也就是最优转移点,一定不是 $P_2$。
如果使 $P_3$ 截距最小,$k3\le k$
所以,我们要在单调队列中维护斜率严格递增的一个点集,具体的,维护一个下凸包。这是取 $\min$ 的情况。相同的,取 $\max$ 就得维护一个上凸包。
回顾一下整个流程:
- 如果队列前两个点的斜率小于 $k$,则踢出队首。
- 取队首作为转移点。
- 将转移点 $i$ 入队之前,维护一个斜率递增的单调队列。如果下降,则弹出队尾。
代码就很简单了。
Code:
1 |
|
三、例题讲解
P2365 任务安排
虽然可以用 $O(n^2)$ 草过去,但是为了巩固斜率优化还是推荐大家再写一遍。
容易想到 $n^2 dp:$
$$
f_i=\min\limits_{0\le j<i}{f_j+s\times(sumC_n-sumC_j)+sumT_i\times(sumC_i-sumC_j)}
$$
看到有 $i$ 和 $j$ 的乘积,考虑斜率优化:
$$
f_i={f_j+s\times sumC_n-s\times sumC_j+sumT_i\times sumC_i-sumT_i\times sumC_j)}
$$
与 $j$ 有关的扔到右边:
$$
\begin{matrix}\underbrace{sumT_i+s}\k\end{matrix}\times \begin{matrix}\underbrace{sumC_j}\x\end{matrix}+\begin{matrix}\underbrace{f_i-sumT_i\times sumC_i}\b\end{matrix}=\begin{matrix}\underbrace{f_j+s\times sumC_n}\y\end{matrix}
$$
经过猜想+测试,以下整理也是可以的:
$$
\begin{matrix}\underbrace{sumT_i}\k\end{matrix}\times \begin{matrix}\underbrace{sumC_j}\x\end{matrix}+\begin{matrix}\underbrace{f_i-sumT_i\times sumC_i}\b\end{matrix}=\begin{matrix}\underbrace{f_j+s\times sumC_n-s\times sumC_j}\y\end{matrix}
$$
换言之,我们只需要把 $i,j$ 相关的相乘的项作为斜率 $k$ 。
Code:
1 |
|
咕咕咕