题目链接
设(d[i])为将前 (i) 个玩具装入箱中所需得最小费用
容易得到动态转移方程:
[d[i] = min(d[j] + (s[i]-s[j]+i-j-1-L)^2), (j<i)
]
其中(s[i] = sum_1^iC[i]),普通DP复杂度为(O(n^2))。经过斜率优化后将变为(O(n))。
仔细观察我们便于表示可以令(f[i] = s[i]+i)
那么式子变成了
[d[i] = min(d[j] + (f[i]-f[j]-1-L)^2)
]
我们讨论(j_1,j_2(1le j_1< j_2<i))决策,假设(j_2)要比(j_1)更优,那么有
(d[j_1] + (f[i] -f[j_1]-1-L)^2 ge d[j_2]+(f[i]-f[j_2]-1-L)^2)
展开后得到
(d[j_1] + f[i]^2 - 2 imes f[i] imes (f[j_1]+1+L)+(f[j_1]+1+L)^2 ge d[j_2]+f[i]^2-2 imes f[i] imes (f[j_2]+1+L)+(f[j_2]+1+L)^2)
移项后可得
(2cdot f[i]ge {d[j_2]+(f[j_2]+1+L)^2-d[j_1]-(f[j_1]+1+L)^2 over f[j_2]-f[j_1]})
令(g[i] = f[i]+1+L), 则有
(2cdot f[i]ge {(d[j_2]+g[j_2])-(d[j_1]+g[j_1])over f[j_2]-f[j_1]})
所以用一个队列维护决策集,当(j_1<j_2),并且上式满足时,(j_1) 出队。
又由于(f[i])随(i)单调递增。所以计算(d[i])之后要将 (i) 入队时,要及时排除掉不可能作为决策的元素。
如何计算?队尾的斜率也要满足单调性,保持跟(f[i])的单调性一致即可。
#include <bits/stdc++.h>
using namespace std;
const int N = 50010;
typedef long long ll;
typedef long double db;
db c[N],d[N],f[N],s[N],g[N];
int n,L;
int q[N],l,r;
db sqr(db x){return x * x;}
db slope(int i,int j){
return ((d[i] + g[i]) - (d[j] + g[j])) / (f[i] - f[j]);
}
int main(){
scanf("%d%d",&n,&L);
l=r=1;
for(int i=1;i<=n;i++){
cin>>c[i];
s[i]=s[i-1] + c[i];
f[i] = s[i] + i;
g[i] = (f[i] + 1 + L) * (f[i] + 1 + L);
}
g[0] = (ll)(1+L)*(1+L);//注意0号元素的g值初始化
for(int i=1;i<=n;i++){
while(l < r && slope(q[l],q[l+1]) < 2 * f[i])l++;
int j = q[l];
d[i] = d[j] + sqr(f[i]-f[j]-1-L);
while(l < r && slope(q[r],q[r-1]) > slope(i,q[r-1]))r--;//满足队尾斜率单调性
q[++r] = i;//入队
}
printf("%lld
",(ll)d[n]);
return 0;
}