文章同步发表于我的 洛谷博客。
若一个函数 f ( x ) f(x) f(x) 具有单调性,且自变量的定义域为 x ∈ [ l , r ] x\in [l,r] x∈[l,r],则必然存在以下分界点 P P P,使得:
{ ∀ x ∈ [ l , P ] , f ( x ) = true ∀ x ∈ ( P , r ] , f ( x ) = false \begin{cases}\forall\ x\in [l,P],\ f(x)=\operatorname{true}\\\forall\ x\in (P,r],\ f(x)=\operatorname{false}\end{cases} { ∀ x∈[l,P], f(x)=true∀ x∈(P,r], f(x)=false
因此,对于区间 [ l , r ] [l,r] [l,r],中点为 m i d = l + r 2 mid=\dfrac{l+r}2 mid=2l+r,若 f ( m i d ) = true f(mid)=\operatorname{true} f(mid)=true, 则 ∀ x ∈ [ l , m i d ] , f ( x ) = true \forall\ x\in [l,mid],\ f(x)=\operatorname{true} ∀ x∈[l,mid], f(x)=true,最优决策点必然在 m i d mid mid 右侧,即可令 l ← m i d + 1 l\leftarrow mid+1 l←mid+1(在整数域上的二分,实数域则 l ← m i d l\leftarrow mid l←mid)。
这样,整体复杂度为 O ( t i m e s × c h e c k ) O(times\times check) O(times×check), t i m e s times times 表示二分次数,通常为 log n \log n logn,而 c h e c k check check 为单次判定的复杂度。
inline int Binary(int l,int r){
//整数域的二分
int res=0;
while(l<=r){
int mid=(l+r)>>1;
if(check(mid)) res=mid,l=mid+1;
else r=mid-1;
}
return res;
}
inline double Binary_R(double l,double r){
//实数域二分
double res=0;
while(r-l>=eps){
double mid=(l+r)/2.0;
if(check(mid)) res=l=mid;
else r=mid;
}
return res;
}
顾名思义,单调队列是一种内部元素单调的特殊双端队列,常用于定长的区间最值问题。
例题:P1886 滑动窗口。
这里就不展开讲了,因为斜率优化会大量基于单调队列来实现。
在初中数学中,大家都应该学习过。对于平面内两点 ( x 1 , y 1 ) , ( x 2 , y 2 ) (x_1,y_1),(x_2,y_2) (x1,y1),(x2,y2),其联结所得的直线的斜率为 k = y 1 − y 2 x 1 − x 2 k=\dfrac{y_1-y_2}{x_1-x_2} k=x1−x2y1−y2。
有一类的动态规划,其状态转移方程具有以下的特性: f i = min { f j − a i × b j } f_i=\min\{f_j-a_i\times b_j\} fi=min{ fj−ai×bj},即同时具有含有 i , j i,j i,j 的项 a i , b j a_i,b_j ai,bj。此时,单纯地单调队列优化 DP 就无法使用,而暴力枚举的复杂度是 O ( n 2 ) O(n^2) O(n2)。
那么,有没有更优的方法,甚至做到 O ( n ) O(n) O(n) 的呢?
斜率优化的标志通常是(个人总结):
状态通常是一维(当然也可能会出现二维)。
状态转移方程出现 a i × b j a_i\times b_j ai×bj 的项(这就排除了单调队列优化 DP 的可能)。
n ≤ 1 0 6 n\le 10^6 n≤106,即算法复杂度是 O ( n ) O(n) O(n)。
在这种情况下,就可以往斜优的方向想了。斜优的方法通常是:对于直线解析式 y = k x + b y=kx+b y=kx+b,将只关于 i i i 的部分看作定值 b b b,将只关于 j j j 的部分看作因变量 y y y,将 a i × b j a_i\times b_j ai×bj 的项看作 k x kx kx,其中 i i i 对应 k k k, j j j 对应 x x x。
这样,平面内的点 ( x , y ) (x,y) (x,y) 只与 j j j 有关,且只有因变量 y y y 包含 f j f_j fj,因此 ( x , y ) (x,y) (x,y) 即对应可能的决策点; k , b k,b k,b 与 i i i 有关,且仅有 b b b 包含 f i f_i fi,因此最优化 b b b。
例如上述的状态转移方程,将 min \min min 去掉得:
f i = f j − a i × b j , f j = a i × b j + f i f_i=f_j-a_i\times b_j,\ f_j=a_i\times b_j+f_i fi