【bzoj1010】【HNOI2008】【玩具装箱】【toy】【动态规划】【斜率优化】

题目大意:

有编号为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
#include
#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((hget(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((hget(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;
}

你可能感兴趣的:(动态规划,bzoj)