算法介绍
使用条件
形如
d
p
[
i
]
=
m
i
n
{
A
(
i
)
×
B
(
j
)
+
C
(
i
)
+
D
(
j
)
}
dp[i]=min\{A(i)\times B(j)+C(i)+D(j)\}
dp[i]=min{A(i)×B(j)+C(i)+D(j)}的转移方程可尝试使用斜率优化。
(取
m
a
x
max
max是类似的,本文不赘述)
法一:线性规划
考虑把 d p [ i ] = A ( i ) × B ( j ) + C ( i ) + D ( j ) dp[i]=A(i)\times B(j)+C(i)+D(j) dp[i]=A(i)×B(j)+C(i)+D(j) 化成 y = k x + b y=kx+b y=kx+b的形式,其中 y , x y,x y,x只与 j j j有关, k , b k,b k,b只与 i i i有关,且 x x x严格单调递增。
则有:
D
(
j
)
=
−
A
(
i
)
×
B
(
j
)
+
d
p
[
i
]
−
C
(
i
)
D(j)=-A(i)\times B(j)+dp[i]-C(i)
D(j)=−A(i)×B(j)+dp[i]−C(i)
其中
y
=
D
(
j
)
,
k
=
−
A
(
i
)
,
x
=
B
(
j
)
,
b
=
d
p
[
i
]
−
C
(
i
)
y=D(j),k=-A(i),x=B(j),b=dp[i]-C(i)
y=D(j),k=−A(i),x=B(j),b=dp[i]−C(i)
在平面直角坐标系上把所有的
(
x
j
,
y
j
)
(x_j,y_j)
(xj,yj)(即
(
B
(
j
)
,
D
(
j
)
)
(B(j),D(j))
(B(j),D(j)))标出来。
要令
d
p
[
i
]
dp[i]
dp[i]最小,则要令
b
b
b最小,即是要找到某一点
(
x
j
,
y
j
)
(x_j,y_j)
(xj,yj),使斜率为
k
k
k的直线经过该点时算出来的
b
b
b最小。
发现只有 在给出点的下凸壳上的点可能成为最优决策点。
法二:代数法(数形结合)
设
j
1
,
j
2
j_1,j_2
j1,j2
(
0
≤
j
1
<
j
2
<
i
)
(0\leq j_1<j_2<i)
(0≤j1<j2<i) 为
d
p
[
i
]
dp[i]
dp[i]的两个决策点,且决策点
j
2
j_2
j2优于
j
1
j_1
j1,则有:
A
(
i
)
×
B
(
j
2
)
+
C
(
i
)
+
D
(
j
2
)
≤
A
(
i
)
×
B
(
j
1
)
+
C
(
i
)
+
D
(
j
1
)
A(i)\times B(j_2)+C(i)+D(j_2)\leq A(i)\times B(j_1)+C(i)+D(j_1)
A(i)×B(j2)+C(i)+D(j2)≤A(i)×B(j1)+C(i)+D(j1)
化简式子,使不等号左边只与
i
i
i有关,不等号右边只与
j
1
,
j
2
j_1,j_2
j1,j2有关:
A
(
i
)
≤
−
D
(
j
2
)
−
D
(
j
1
)
B
(
j
2
)
−
B
(
j
1
)
A(i)\leq -\frac{D(j_2)-D(j_1)}{B(j_2)-B(j_1)}
A(i)≤−B(j2)−B(j1)D(j2)−D(j1)
−
A
(
i
)
≥
D
(
j
2
)
−
D
(
j
1
)
B
(
j
2
)
−
B
(
j
1
)
-A(i)\geq \frac{D(j_2)-D(j_1)}{B(j_2)-B(j_1)}
−A(i)≥B(j2)−B(j1)D(j2)−D(j1)
设
x
j
=
B
(
j
)
,
y
j
=
D
(
j
)
x_j=B(j),y_j=D(j)
xj=B(j),yj=D(j),则
D
(
j
2
)
−
D
(
j
1
)
B
(
j
2
)
−
B
(
j
1
)
\frac{D(j_2)-D(j_1)}{B(j_2)-B(j_1)}
B(j2)−B(j1)D(j2)−D(j1)可以看作
P
j
1
(
x
j
1
,
y
j
1
)
P_{j_1}(x_{j_1},y_{j_1})
Pj1(xj1,yj1),
P
j
2
(
x
j
2
,
y
j
2
)
P_{j_2}(x_{j_2},y_{j_2})
Pj2(xj2,yj2)两点连线的斜率。
也就是说,如果存在两个决策点 j 1 , j 2 j_1,j_2 j1,j2满足 0 ≤ j 1 < j 2 < i 0\leq j_1<j_2<i 0≤j1<j2<i,使得 P j 1 P_{j_1} Pj1, P j 2 P_{j_2} Pj2两点连线的斜率 ≤ − A ( i ) \leq -A(i) ≤−A(i),那么决策点 j 2 j_2 j2优于 j 1 j_1 j1。
于是,对于这样子的三个点,可证
B
B
B一定不是最优决策点。
我们可以将
B
B
B从候选决策点中踢出去(删除),只留下
A
A
A和
C
C
C,删后的情况如下图所示:
最终,我们维护的候选决策点应该构成了一个下凸壳:
实现
按 x j x_j xj的单调性分类
- x j x_j xj递增(递减的话给 x x x取个负就是递增了):单调队列维护凸包
- x j x_j xj不单调:平衡树维护凸包/cdq分治提供单调性
按 k i k_i ki的单调性分类
- k i k_i ki递增: 只需要维护部分凸包。即用单调队列维护凸包时可以不断弹掉队首,删掉不需要维护的凸包;查询答案时直接取队首即可。
- k i k_i ki不递增:必须维护完整凸包。即用单调队列维护凸包时不能再弹掉队首了(即用单调栈维护凸包);查询答案时二分找最优决策点。
实现细节
- 当
x
j
x_j
xj 非严格递增时,在求斜率时可能会出现
x
j
1
=
x
j
2
x_{j1}=x_{j2}
xj1=xj2 的情况,此时最好写成这样的形式:
return Y(j)>=Y(i)?inf:-inf
,而不要直接返回 i n f inf inf 或者 − i n f −inf −inf - 手写队列的初始化是
h=1,t=0
,由于队列初始化大多都要塞入一个点 P ( 0 ) P(0) P(0),所以在一些题解中可以看到h=t=1
- 手写队列判断不为空的条件是
h<=t
,而出入队判断都需要有至少 2 两个元素才能进行操作。所以应是h<t
。 - 计算斜率可能会因为向下取整而出现误差,所以 s l o p e slope slope 函数最好设为 l o n g d o u b l e long double longdouble 类型。
- 在比较两个斜率时,尽量写上等于,即
<=
和>=
而不是<
和>
。这样写对于去重有奇效(有重点时会导致斜率分母出锅),但不要以为这样就可以完全去重,因为要考虑的情况可能会非常复杂,所以还是推荐加上 1. 中提到的特判,确保万无一失。