题目大意:
有编号为1..N的N件玩具,第i件玩具长度是Ci。可以将任意编号连续的玩具变成一堆,再装到箱中。如果一堆中有多个玩具,那么每两件玩具之间要加入1个单位长度的填充物。
如果将第i到第j件玩具放在一堆中,那长度将为j-i+sigma(Ck)//i<=k<=j
制作箱的费用与箱长度有关。如果容器长度为x,其制作费用为(x-L)^2。可以制造出任意长度的箱,求最小费用。
解题思路:
设f[i]为装到第i件玩具的最小费用,显然可见
f[i]=min(f[j]+(sigma(ck)//j+1<=k<=i//+i-j-1-l)^2)
设s[i]=sigma(ck)//1<=k<=i
则f[i]=min(f[j]+(s[i]-s[j]+i-j-1-l)^2)
设g[i]=s[i]+i,c=l+1
则f[i]=min(f[j]+(g[i]-g[j]-c)^2)
则f[i]=min(f[j]+g[i]^2+g[j]^2+c^2-2g[i]g[j]-2g[i]c+2g[j]c)
考虑从f[j],f[k]j小于k,分别转移到f[i]的递推式,若从f[j]转移劣于从f[j]转移则一定满足
f[j]+g[i]^2+g[j]^2+c^2-2g[i]g[j]-2g[i]c+2g[j]c>f[k]+g[i]^2+g[k]^2+c^2-2g[i]g[k]-2g[i]c+2g[k]c
即f[j]+g[j]^2-2g[i]g[j]+2g[j]c>f[k]+g[k]^2-2g[i]g[k]+2g[k]c
即(f[j]+g[j]^2+2g[j]c)-(f[k]+g[k]^2+2g[k]c)>2g[i]*(g[j]-g[k])
因为j小于k,所以g[j]-g[k]<0
即((f[j]+g[j]^2+2g[j]c)-(f[k]+g[k]^2+2g[k]c))/(g[j]-g[k])<2g[i]
因为除法精度问题处理时须还原上一条式再判断
可见从f[j]转移或从f[j]转移于i没有半毛钱关系,至此我们想到了斜率优化动态规划。建立一个(f[j]+g[j]^2+2g[j]c,g[j])的点阵,用单调队列维护其单调性。
设函数h(j,k)=((f[j]+g[j]^2+2g[j]c)-(f[k]+g[k]^2+2g[k]c))/(g[j]-g[k])
当h(队头,队头的下一位)大于2g[i],则队头不是最优,将其踢掉
当h(队尾的前一位,队尾)大于h(队尾,i),则队尾比将要加入的i劣,将其踢掉
显然队头为当前最优,用其更新f[i]即可
具体严谨证明不在此给出,请参考其他文献
参考代码
#include<cstdio>
#include<string>
#define ll long long
#define fo(i,j,k) for(ll i=j;i<=k;i++)
using namespace std;
ll const maxn=50000;
ll n,l,s[maxn+10],f[maxn+10],que[maxn+10];
inline ll get() {
char ch;
while (!isdigit(ch=getchar()));
ll v=ch-48;
while (isdigit(ch=getchar())) v=v*10+ch-48;
return v;
}
inline void scan(){
n=get();l=get()+1;
fo(i,1,n)
s[i]=s[i-1]+get();
}
inline ll get(ll x){
return f[x]+(s[x]+x)*(s[x]+x)+2*l*(s[x]+x);
}
inline void solve(){
ll h=0,t=0;
fo(i,1,n){
while((h<t)&&(get(que[h])-get(que[h+1])>2*(s[i]+i)*(s[que[h]]+que[h]-s[que[h+1]]-que[h+1])))h++;
f[i]=f[que[h]]+(s[i]+i-s[que[h]]-que[h]-l)*(s[i]+i-s[que[h]]-que[h]-l);
while((h<t)&&((get(que[t-1])-get(que[t]))*(s[que[t]]+que[t]-s[i]-i)>(get(que[t])-get(i))*(s[que[t-1]]+que[t-1]-s[que[t]]-que[t])))t--;
que[++t]=i;
}
printf("%lld",f[n]);
}
int main(){
scan();
solve();
return 0;
}