仿佛终于理解了斜率优化
Address
https://www.lydsy.com/JudgeOnline/problem.php?id=1096
Solution
显然 dp 。
定义状态:
f[i]
f
[
i
]
表示在前
i
i
个工厂建立仓库,且第 个工厂必须建立仓库的最小费用。
转移就是枚举上一个仓库的建立位置
j
j
(如果没有则 )
f[i]=minj=0i−1{f[j]+∑k=j+1i{(Xi−Xk)×Pk}+Ci}
f
[
i
]
=
min
j
=
0
i
−
1
{
f
[
j
]
+
∑
k
=
j
+
1
i
{
(
X
i
−
X
k
)
×
P
k
}
+
C
i
}
考虑把 ∑ik=j+1{(Xi−Xk)×Pk} ∑ k = j + 1 i { ( X i − X k ) × P k } 化开:
( S S 为 的前缀和, R R 为 的前缀和)
(即 Si=∑ij=1Pj S i = ∑ j = 1 i P j , Ri=∑ij=1{Pj×Xj} R i = ∑ j = 1 i { P j × X j } )
∑k=j+1i{(Xi−Xk)×Pk}=∑k=j+1i{(Xi×Pk−Xk×Pk}
∑
k
=
j
+
1
i
{
(
X
i
−
X
k
)
×
P
k
}
=
∑
k
=
j
+
1
i
{
(
X
i
×
P
k
−
X
k
×
P
k
}
=∑k=j+1i{Xi×Pk}−∑k=j+1i{Xk×Pk}
=
∑
k
=
j
+
1
i
{
X
i
×
P
k
}
−
∑
k
=
j
+
1
i
{
X
k
×
P
k
}
=Xi×∑k=j+1iPk−∑k=j+1i{Xk×Pk}
=
X
i
×
∑
k
=
j
+
1
i
P
k
−
∑
k
=
j
+
1
i
{
X
k
×
P
k
}
=Xi×(Si−Sj)−(Ri−Rj)
=
X
i
×
(
S
i
−
S
j
)
−
(
R
i
−
R
j
)
于是:
f[i]=minj=0i−1{f[j]+Xi×(Si−Sj)−(Ri−Rj)+Ci}
f
[
i
]
=
min
j
=
0
i
−
1
{
f
[
j
]
+
X
i
×
(
S
i
−
S
j
)
−
(
R
i
−
R
j
)
+
C
i
}
=minj=0i−1{f[j]+Xi×Si−Xi×Sj−Ri+Rj+Ci}
=
min
j
=
0
i
−
1
{
f
[
j
]
+
X
i
×
S
i
−
X
i
×
S
j
−
R
i
+
R
j
+
C
i
}
=minj=0i−1{f[j]+Rj−Xi×Sj}+Xi×Si−Ri+Ci
=
min
j
=
0
i
−
1
{
f
[
j
]
+
R
j
−
X
i
×
S
j
}
+
X
i
×
S
i
−
R
i
+
C
i
这是一个显然的斜率优化。
设 k<j<i k < j < i ,那么从 j j 转移比 优当且仅当:
f[j]+Rj−Xi×Sj<f[k]+Rk−Xi×Sk
f
[
j
]
+
R
j
−
X
i
×
S
j
<
f
[
k
]
+
R
k
−
X
i
×
S
k
(f[j]+Rj)−(f[k]+Rk)<Xi×Sj−Xi×Sk
(
f
[
j
]
+
R
j
)
−
(
f
[
k
]
+
R
k
)
<
X
i
×
S
j
−
X
i
×
S
k
(f[j]+Rj)−(f[k]+Rk)Sj−Sk<Xi
(
f
[
j
]
+
R
j
)
−
(
f
[
k
]
+
R
k
)
S
j
−
S
k
<
X
i
左边是一个斜率的式子!
把每个决策看作一个平面上一个点:决策为 i i 为 。
考虑 a<b<c a < b < c ,设 slope[a,b] s l o p e [ a , b ] 为过第 a a 个点与第 个点的直线的斜率,
并且 slope[a,b]>slope[b,c] s l o p e [ a , b ] > s l o p e [ b , c ] 。
(1)如果 slope[a,b]<Xi s l o p e [ a , b ] < X i ,那么 slope[b,c] s l o p e [ b , c ] 一定也小于 Xi X i 。这时候决策 c c 比决策 优。
(2)如果 slope[b,c]>Xi s l o p e [ b , c ] > X i ,那么 slope[a,b] s l o p e [ a , b ] 一定也大于 Xi X i 。那么这时候决策 b b 一定不比决策 优。
(3)如果 slope[b,c]<Xi<slope[a,b] s l o p e [ b , c ] < X i < s l o p e [ a , b ] ,那么决策 b b 一定不比决策 和 c c 优。
得出结论:如果 ,那么决策 b b 一定不会是最优决策。
所以,所有可能成为最优决策的点构成一个下凸壳,斜率单调递增。
并且,如果一个决策 在下凸壳上是最优决策(设 k k 为 在下凸壳上的前一个点, h h 为 在凸壳上的后一个点),那么一定有 slope[k,j]<Xi,slope[j,h]>Xi s l o p e [ k , j ] < X i , s l o p e [ j , h ] > X i 。从这里也能得出,这个动规方程在下凸壳上满足决策单调性。
根据斜率的单调性,可以用一个单调队列维护下凸壳,每次进行决策时,利用决策的单调性,从队首不断出队,找到最优决策 j j 并转移。每次决策结束后,在下凸壳末尾加入点 ,并维护下凸的性质。
由于每个点最多进出队一次,所以复杂度 O(n) O ( n ) 。
Code
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define For(i, a, b) for (i = a; i <= b; i++)
using namespace std;
inline int read() {
int res = 0; bool bo = 0; char c;
while (((c = getchar()) < '0' || c > '9') && c != '-');
if (c == '-') bo = 1; else res = c - 48;
while ((c = getchar()) >= '0' && c <= '9')
res = (res << 3) + (res << 1) + (c - 48);
return bo ? ~res + 1 : res;
}
typedef long long ll; const int N = 1e6 + 5;
int n, X[N], P[N], C[N], H, T, que[N]; ll f[N], S[N], R[N], x[N], y[N];
ll calc(int j, int i) {return y[j] - x[j] * X[i];}
double slope(int j, int k) {
return 1.0 * (y[j] - y[k]) / (x[j] - x[k]);
}
int main() {
int i; n = read(); For (i, 1, n) X[i] = read(), P[i] = read(), C[i] = read();
For (i, 1, n) S[i] = S[i - 1] + P[i], R[i] = R[i - 1] + 1ll * P[i] * X[i];
f[0] = x[0] = y[0] = 0; que[H = T = 1] = 0; For (i, 1, n) {
while (H < T && calc(que[H], i) >= calc(que[H + 1], i)) H++;
f[i] = calc(que[H], i) + 1ll * S[i] * X[i] - R[i] + C[i];
x[i] = S[i]; y[i] = f[i] + R[i];
while (H < T && slope(que[T - 1], que[T]) > slope(que[T], i)) T--;
que[++T] = i;
}
cout << f[n] << endl; return 0;
}