斜率优化dp

发布时间 2023-07-24 20:05:04作者: star_road_xyz

### 斜率优化简介

**问题引入**

给定一个长度为 $n$ 的序列 $a[i]$ ,连续若干个数可以分为一组,将这些数分成若干组,每一组的代价为组内元素和的平方,要求最小化代价

$n\le 2\times 10^5$

**朴素算法**

设 $f[i]$ 表示将前 $i$ 个数分组之后的最小代价,那么有转移方程
$$
f[i]=\min_{1\le j\le i-1}\{f[j]+(\text{pre}[i]-\text{pre}[j])^2\}
$$
如果我们将后面那个平方项拆开,那么会出现 $\text{pre}[i]\times \text{pre}[j]$ 这样的同时包含两个变量的项,这就意味着我们将无法采用上面单调队列的方法来优化它,这时候我们就要引入斜率优化了

**基本概念**

就是把决策与决策之间写成一个类似斜率 $\frac{y_1-y_2}{x_1-x_2}$ 的式子,然后对其单调性进行分析,用队列维护决策


### 任务安排1 (费用提前计算+斜率优化)

**题目**

有 $N$ 个任务排成一个序列在一台机器上等待执行,它们的顺序不得改变。

机器会把这 $N$ 个任务分成若干批,每一批包含连续的若干个任务。
从时刻 $0$ 开始,任务被分批加工,执行第 $i$ 个任务所需的时间是 $T_i$。

另外,在每批任务开始前,机器需要 $S$ 的启动时间,故执行一批任务所需的时间是启动时间 $S$ 加上每个任务所需时间之和。

一个任务执行后,将在机器中稍作等待,直至该批任务全部执行完毕。
也就是说,同一批任务将在同一时刻完成。

每个任务的费用是它的完成时刻乘以一个费用系数 $C_i$。

请为机器规划一个分组方案,使得总费用最小。

$1\le n\le 3\times 10^5,1\le S,C_i,T_i\le 512$

**题解**

设 $f[i][j]$ 表示前 $i$ 个任务分成 $j$ 批时的最小费用
$$
f[i][j]=\min_{0\le k < j } \{ f[k][j-1]+(S\times j+\text{sumT}[i])\times (\text{sumC}[i]-\text{sumC}[k]) \}
$$
时间复杂度 $\mathcal O(n^3)$,我们无法接受

这时候我们可以采用一个经典套路:费用提前计算

就是说,我们在执行一些任务的时候,我们不方便知道这个机器在之前启动了多少次,因此需要记一个状态 $j$ ,但我们知道当前机器的费用是会在后面每一个机器中算到的,因此采用和关路灯和基站选址相同的方法,我们可以这么做:

设 $f[i]$ 表示把前 $i$ 个任务分成若干批次,并且加上每一批造成的后续影响的最小费用。

转移方程如下
$$
f[i]=\min_{0\le j < i} \{ f[j]+\text{sumT}[i]\times (\text{sumC}[i]-\text{sumC}[j] ) +S\times (\text{sumC}[n]-\text{sumC[j]})\}
$$

但是这样依然无法通过本题,所以下面开始进行斜率优化

我们先对状态转移方程进行变形,先将与 $j$ 有关的项提出来
$$
f[i]=\min\{f[j]-(S+\text{sumT}[i])\times \text{sumC}[j]\}+\text{sumC}[i]\times \text{sumT}[i]+S\times \text{sumC}[n]
$$
去掉 $\min$,将与 $j$ 有关的量视作是变量,有

$$
f[j]=(S+\text{sumT}[i])\times \text{sumC}[j]+f[i]-\text{sumT}[i]\times \text{sumC}[i]-S\times \text{sumC}[n]
$$

那么这个东西就变成了一个以 $S+\text{sumT}[i]$ 为斜率,$f[i]-\text{sumT}[i]\times \text{sumC}[i]-S\times \text{sumC}[n]$ 为截距的直线,同时,每一个决策 $j$ 都对应着二维平面上的一个点 $(\text{sumC}[j],f[j])$ 。

同时,$f[i]$ 与其对应的直线在平面上的截距有关,斜率是固定的为 $S+\text{sumT}[i]$ ,于是我们知道,截距越小,$f[i]$ 就越小,直观来讲,就是平面上有一堆的点,然后你现在要拿着一个斜率固定的直线去卡到点集的最低位置,那个就是当前状态 $i$ 的最优决策

我们考虑如何来快速找到这个最低点,设 $j_1< j_2< j_3$,由于 $T,C$ 都是正整数,所以这里有 $\text{sumC}[j_1]<\text{sumC}[j_2]<\text{sumC}[j_3]$

给出结论,$j_2$ 能够是最优决策,当且仅当
$$
\frac{f[j_2]-f[j_1]}{\text{sumC}[j_2]-\text{sumC}[j_1]}<\frac{f[j_3]-f[j_2]}{\text{sumC}[j_3]-\text{sumC}[j_2]}
$$

几何意义就是 $j_2$ 处在点集的下凸壳上,换句话说,对于这道题而言,只有处于下凸壳上的点才有可能成为最优决策点

综上,我们可以建立一个单调队列 $q$ 来维护这个下凸壳,队列中存储对应的决策,然后对于每个状态 $i$ :
1. 检查队头的两个决策 $q[l],q[l+1]$, 如果其所对应的斜率 $\frac{f[q[l+1]]-f[q[l]]}{\text{sumC}[q[l+1]]-\text{sumC}[q[l]]}\le S+\text{sumT}[i]$ ,那么就让 $q[l]$ 出队,因为其对应的点一定不是直线 $i$ 与下凸壳的切点(画图理解)
2. 取队头 $q[l]$ 作为最优决策,算出 $f[i]$
3. 将新决策 $i$ 插入队尾,利用上面给出的判定可能最优决策的条件,维护队列的单调性

时间复杂度 $\mathcal O(n)$

### 任务安排2 (状态斜率不单增的斜率优化)

**题目**

同任务安排1,区别在于 $-512\le T\le 512$

**题解**

$T$ 可以取到负值,说明我们上面所提到的状态直线斜率变化不在具有单调性,这时候我们可以直接用单调队列先维护出整个下凸壳,然后对于每一个状态,利用二分查找,找到左边斜率比它小,右边斜率比它大的分界点,即最优决策点

时间复杂度 $\mathcal O(n\log n)$

### 任务安排3 (决策点横坐标不单增的斜率优化)

**题目**

同任务安排2,同时再更改一个条件 $C$ 可能是负数

**题解**

这道题把 $\text{sumC}$ 的单调性也做掉了,由于状态 $i$ 只能从比它小的状态 $j$ 转移过来,因此我们考虑在这种情况下如何动态维护下凸壳

假设我们现在想要插入一个决策 $j$ ,它的坐标为 $(\text{sumC}[j],f[j])$, 画个图模拟一下,我们发现决策 $j$ 的加入会替代掉原下凸壳的一段连续的决策(或者不替代),所以我们可以考虑从决策点 $j$ 处向左右两侧进行二分,向左找到第一个斜率比它小的边,向右找到第一个斜率比它大的边,然后从下凸壳中删除掉我们选出的这一段决策点(这里通过平衡树维护),最后通过任务安排2的方法寻找当前状态 $i$ 的最优决策点即可

由于每个决策点至多只能加入删除一次,因此时间复杂度是 $\mathcal O(n\log n)$ 的

### CF311B 猫的运输 (二维dp的斜率优化)

**题目**

他养了 $m$ 只可爱的猫子,雇佣了 $p$ 个铲屎官。这里有一条又直又长的道路穿过了农场,有 $n$ 个山丘坐落在道路周围,编号自左往右从 $1$ 到 $n$。山丘 $i$ 与山丘 $i-1$ 的距离是 $D_i$ 米。铲屎官们住在 $1$ 号山丘。

一天,猫子们外出玩耍。猫子 $i$ 去山丘 $H_i$ 游玩,在 $T_i$ 时间结束他的游玩,然后在山丘 $H_i$ 傻等铲屎官。铲屎官们必须把所有的猫子带上。每个铲屎官直接从 $H_1$ 走到 $H_n$,中间不停下,可以认为不花费时间的把游玩结束的猫子带上。每个铲屎官的速度为一米每单位时间,并且足够强壮来带上任意数量的猫子。

你的任务是安排每个铲屎官出发的时间(可以从 0 时刻之前出发),最小化猫子们等待的时间之和。

对于全部的数据,满足 $2\le n\le 10^5$,$1\le m\le10^5$,$1\le p\le 100$,$1\le D_i<10^4$,$1\le H_i\le n$,$0\le t\le10^9$。

**题解**

首先对于每一只猫,我们可以计算出什么时候可以刚好将它接走,我们将这些时间存在一个长度为 $m$ 的数组 $t$ 当中,将 $t$ 从小到大排序,记 $s$ 数组为 $t$ 数组的前缀和

这时候有一个显然的贪心策略是,每个人需要带走 $t$ 数组中一段连续区间的猫才有可能最优

这样问题就转化成了,将一个长度为 $n$ 的序列分为 $p$ 段,每一段的花费是该段的最大值与其他所有值的差值之和,求最小代价

设 $f[i][j]$ 表示考虑到第 $i$ 只猫,已经划分了 $j$ 段时的最小代价

由此我们得出转移方程如下:
$$
f[i][j]=\min_{k=0}^{i-1}\{f[k][j-1]+t[i]\times (i-k)-s[i]+s[k]\}
$$

我们先忽略掉 $j$ 这一维,那么转移方程就是

$$
f[i]=\min_{j=0}^{i-1}\{f[j]+t[i]\times (i-j)-s[i]+s[j]\}
$$

这显然可以进行斜率优化,直线斜率是 $t[i]$,决策点坐标是 $(j,s[j]+f[j])$, 值都是正的,按照任务安排1去维护下凸壳即可

对于段数的限制,我们可以考虑外层枚举状态 $i$,内层从小到大枚举划分段数 $j$ 按照转移方程进行转移即可

时间复杂度 $\mathcal O(pm)$

### P3628 [APIO2010] 特别行动队 (上凸壳斜率优化)

**题目**

你有一支由 $n$ 名预备役士兵组成的部队,士兵从 $1$ 到 $n$ 编号,你要将他们拆分成若干特别行动队调入战场。出于默契的考虑,同一支特别行动队中队员的编号**应该连续**,即为形如 $(i, i + 1, \cdots i + k)$的序列。所有的队员都应该属于且仅属于一支特别行动队。

编号为 $i$ 的士兵的初始战斗力为 $x_i$ ,一支特别行动队的初始战斗力 $X$ 为队内士兵初始战斗力之和,即 $X = x_i + x_{i+1} + \cdots + x_{i+k}$。

通过长期的观察,你总结出对于一支初始战斗力为 $X$ 的特别行动队,其修正战斗力 $X'= aX^2+bX+c$,其中 $a,~b,~c$ 是已知的系数($a < 0$)。 作为部队统帅,现在你要为这支部队进行编队,使得所有特别行动队的修正战斗力之和最大。试求出这个最大和。

$1 \leq n \leq 10^6$,$-5 \leq a \leq -1$,$-10^7 \leq b \leq 10^7$,$-10^7 \leq c \leq 10^7$,$1 \leq x_i \leq 100$。

**题解**

我们定义 $s$ 数组是 $x$ 数组的前缀和,设 $f[i]$ 表示将前 $i$ 个士兵分组后的最大和,那么有转移
$$
f[i]=\max_{1\le j\le i-1}\{a(s[i]-s[j])^2+b(s[i]-s[j])+c\}
$$
然后我们得到状态 $i$ 的斜率是 $2a\times s[i]$ ,截距是 $f[i]-a\times s[i]^2-b\times s[i]-c$,对于决策 $j$ ,它在二维平面上的点为 $(s[j],f[j]+a\times s[j]^2-b\times s[j])$

由于这道题是取 $\max$ ,我们改成维护上凸壳即可

时间复杂度 $\mathcal O(n)$