题目链接
题意:有编号为
1
⋯
N
1\cdots N
1⋯N的N件玩具,第 i 件玩具经过压缩后变成一维长度为
C
i
C_i
Ci 。要求在一个容器中的玩具编号是连续的,同时如果将第 i 件玩具到第 j 个玩具放到一个容器中,那么容器的长度将为
x
=
j
−
i
+
∑
k
=
i
j
C
k
x=j-i+\sum\limits_{k=i}^{j}C_k
x=j−i+k=i∑jCk。如果容器长度为 x ,其制作费用为
(
X
−
L
)
2
(X-L)^2
(X−L)2 .其中 L 是一个常量。容器数目长度不限。求最小费用。
1
≤
N
≤
50000
,
1
≤
L
,
C
i
≤
1
0
7
1 \le N \le 50000,1 \le L,Ci \le 10^7
1≤N≤50000,1≤L,Ci≤107
这道题是斜率优化的经典题了qvq
当然dp顺序肯定是从前到后了
分析一下答案式
用f(j)来更新f(i)
X
−
L
=
i
−
(
j
+
1
)
+
∑
k
=
j
+
1
i
C
k
−
L
=
s
u
m
[
i
]
+
i
−
s
u
m
[
j
]
−
j
−
L
−
1
X - L = i-(j + 1)+\sum\limits_{k=j + 1}^{i}C_k - L= sum[i] + i - sum[j] - j - L - 1
X−L=i−(j+1)+k=j+1∑iCk−L=sum[i]+i−sum[j]−j−L−1
设
a
[
i
]
=
s
u
m
[
i
]
+
i
,
b
[
i
]
=
s
u
m
[
i
]
+
i
+
1
+
L
a[i] = sum[i] + i, b[i] = sum[i] + i + 1 + L
a[i]=sum[i]+i,b[i]=sum[i]+i+1+L
f
[
i
]
=
f
[
j
]
+
(
X
−
L
)
2
=
f
(
j
)
+
(
a
[
i
]
−
b
[
j
]
)
2
f[i] = f[j] + (X - L)^2 = f(j) + (a[i] - b[j]) ^ 2
f[i]=f[j]+(X−L)2=f(j)+(a[i]−b[j])2
这里面 随j改变的量是
b
[
j
]
,
b
[
j
]
2
b[j], b[j]^2
b[j],b[j]2和
f
[
j
]
f[j]
f[j]
所以移项得
2
⋅
a
[
i
]
⋅
b
[
j
]
+
f
[
i
]
−
a
[
i
]
2
=
f
[
j
]
+
b
[
j
]
2
2⋅a[i]⋅b[j]+f[i]−a[i]^2=f[j]+b[j]^2
2⋅a[i]⋅b[j]+f[i]−a[i]2=f[j]+b[j]2
将b[j]看作x,
f
[
j
]
+
b
[
j
]
2
f[j]+b[j]^2
f[j]+b[j]2看作y,这个式子就可以看作一条斜率为
2
a
[
i
]
2a[i]
2a[i]的直线
f[i]即当上述直线过点
P
(
b
[
j
]
,
f
[
j
]
+
b
[
j
]
2
)
P(b[j],f[j]+b[j]^2)
P(b[j],f[j]+b[j]2)时,直线在y轴的截距加
a
[
i
]
2
a[i]^2
a[i]2
而题目即为找这个截距的最小值
由于sum[i]随i递增 所以a[i],b[i]都递增
所以点
1
⋯
i
−
1
1 \cdots i-1
1⋯i−1是从左到右排列的
用单调栈维护一下凸包
像做线性规划一样做一个切线就行了
也就是二分斜率(Pj,Pj+1) < 2a[i]
update:貌似不用二分
因为a[i]递增要查询的斜率也递增
那单调队列维护就行了qvq
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int N = 1e5 + 5;
const int K = 2e5;
int n, L;
double sum[N], f[N];
int que[N], head, tail;
inline double a(int x){return sum[x]+x;}
inline double b(int x){return sum[x]+x+1+L;}
inline double X(int x){return b(x);}
inline double Y(int x){return f[x]+b(x)*b(x);}//注意这里不可以用define qvq
//a[i] = sum[i] + i, b[i] = sum[i] + i + 1 + L
//P(b[j],f[j]+b[j]^2)
inline double slope(int x, int y){
return (Y(y) - Y(x)) / (X(y) - X(x));
}
int main() {
scanf("%d%d", &n, &L);
for(int i = 1; i <= n; ++i){
scanf("%lf", &sum[i]);
sum[i] += sum[i - 1];
}
head = tail = 1;
for(int i = 1; i <= n; ++i){
while(head < tail && slope(que[head], que[head + 1]) < 2 * a(i)) ++head;
f[i] = f[que[head]] + (a(i) - b(que[head])) * (a(i) - b(que[head]));
//printf("b %lld\n", (long long)(a(i) - b(que[head])));
while(head < tail && slope(que[tail - 1], que[tail]) > slope(que[tail - 1], i)) --tail;
que[++tail] = i;
}
printf("%lld", (long long)f[n]);
return 0;
}

本文解析了一道关于玩具压缩的斜率优化DP题目,通过分析容器制作费用的数学模型,介绍了如何利用单调栈和斜率优化技巧解决此类问题。文章详细阐述了DP状态转移方程,并给出了C++实现代码。
635





