这道题的初始方程很容易推。
f[i]=min { f[j]+(cost(j+1,i)-L)^2}
其中,f[i]表示前i个东西放在容器里的最小代价
cost表示把j+1到i这些东西都放在一个容器的长度,可以用前缀和s来维护
但是时间上还不够,这时可以用一个斜率优化。
对于j1<j2<i
对于i来说,如果j2这个点更优于j1,那么从今以后j1都没有用了,把它剔除出去。
这样对于我们的取最优值j的范围就缩小了很多很多,就可以在O(n)里解决这道题。
至于如何证明,可以参考网上其它的论文(把j代进DP方程中,列一个不等式。)
对于t>i,s[t]=s[i]+v,这样代进DP里,通过一系列数学方法(加减乘除)易证。
那么最终我们能得到一个式子形如(y2-y1) / (x2-x1) <= 2*(s[i]-L)
左边这便是一次函数中k值的公式,也叫作斜率。
http://blog.sina.com.cn/mektpoy
这人博客中的截距斜率的关系讲的很清楚
那么我们就要维护一个下凸壳,用双向队列实现
#include<cstdio>
#include<cstdlib>
#include<cstring>
long long f[50005];
long long q[50005];
long long s[50005];
double Y(int j)
{
return f[j]+s[j]*s[j];
}
double X(int j)
{
return s[j];
}
double slop(int j1,int j2)
{
return (Y(j2)-Y(j1))/(X(j2)-X(j1));
}
int main()
{
int n,L;
scanf("%d%d",&n,&L);L++;
s[0]=0;
for (int i=1;i<=n;i++)
{
long long soy;
scanf("%lld",&soy);
s[i]=s[i-1]+soy+1;
}
int l=1,r=1;q[1]=0;
for (int i=1;i<=n;i++)
{
while(l<r&&slop(q[l],q[l+1])<=2.0*(s[i]-L))l++;
int j=q[l];
f[i]=f[j]+(s[i]-s[j]-L)*(s[i]-s[j]-L);
while(l<r&&slop(q[r-1],q[r])>slop(q[r],i))r--;
q[++r]=i;
}
printf("%lld",f[n]);
}