P3195 [HNOI2008] 玩具装箱(斜率优化DP)
题目链接
设\(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(1\le 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\times f[i]\times (f[j_1]+1+L)+(f[j_1]+1+L)^2 \ge d[j_2]+f[i]^2-2\times f[i]\times (f[j_2]+1+L)+(f[j_2]+1+L)^2\)
移项后可得
\(2\cdot 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\), 则有
\(2\cdot 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\n",(ll)d[n]);
return 0;
}
注:转载请注明出处