【HNOI2008】玩具装箱
题目描述
P教授要去看奥运,但是他舍不下他的玩具,于是他决定把所有的玩具运到北京。他使用自己的压缩器进行压缩,其可以将任意物品变成一堆,再放到一种特殊的一维容器中。
P教授有编号为(1 cdots n)的(n)件玩具,第(i)件玩具经过压缩后的一维长度为(C_i)。
为了方便整理,P教授要求:
- 在一个一维容器中的玩具编号是连续的。
- 同时如果一个一维容器中有多个玩具,那么两件玩具之间要加入一个单位长度的填充物。形式地说,如果将第(i)件玩具到第(j)个玩具放到一个容器中,那么容器的长度将为(x=j-i+sumlimits_{k=i}^{j}C_k)。
制作容器的费用与容器的长度有关,根据教授研究,如果容器长度为(x),其制作费用为((x-L)^2)。其中(L)是一个常量。P教授不关心容器的数目,他可以制作出任意长度的容器,甚至超过(L)。但他希望所有容器的总费用最小。
输入格式
第一行有两个整数,用一个空格隔开,分别代表(n)和(L)。
第(2)到第((n+1))行,每行一个整数,第((i+1))行的整数代表第(i)件玩具的长度(C_i)。
输出格式
输出一行一个整数,代表所有容器的总费用最小是多少。
样例输入
5 4 3 4 2 1 4
样例输出
1
说明/提示
对于全部的测试点(1 leq n leq 5 imes 10^4),(1 leq L leq 10^7),(1 leq C_i leq 10^7)。
题解
题意:有(n)个物品,每个物品都有长度(C_i),要求吧每个物品装进任意个箱子里使花费最小,给定(L)。
花费计算公式如下:
(x=j-i+sumlimits_{k=i}^{j}C_k)
(ans=(x-L)^2)
看到这里我们首先就想到dp。
转移方程为:
以下(i)均表示(i)到(j)装一个箱子
(dp[j]=min(dp[i-1]+(x(j,i)-l)^2))
(i le j)
(x(j,i)=s[j]-s[i-1]+j-i)
(s[i])表示(sumlimits_{k=1}^{i}C_k)
那么这样计算的话时间复杂度就是(O(n^2))的,显然过不了此题。
那么我们考虑到用斜率优化。
我们假设有(k,i),满足(k lt i le j),且(i)的决策笔(k)的决策优,
那么(k,i)肯定满足:
(dp[i-1]+(s[j]+j-l-(s[i-1]+i))^2<dp[k-1]+(s[k]+k-l-(s[k-1]+k))^2)
我们化简一下上面的式子:
我们令:
(a=s[j]+j-l)
(b[i]=s[i-1]+i)
那么原式化简后就是:
(frac{(dp[i-1]+b[i]^2)-(dp[k-1]+b[k]^2)}{(b[i]-b[k])}<2a)
因为对于每个确定的(j)来说(a)是常数,所以就可以用斜率优化来优化我们的dp了。
用凸包来保证dp的时间复杂度,具体实现看代码。
上代码:
注:代码中i表示i到j装一个箱子,k同上。
#include<bits/stdc++.h>
using namespace std;
int n,L;
int c[50009];
long long dp[50009],s[500009];
int pp[50009],l,r;
long long js(int i,int k){return 1.0*((dp[i-1]+(s[i-1]+i)*(s[i-1]+i))-(dp[k-1]+(s[k-1]+k)*(s[k-1]+k)))/(s[i-1]+i-s[k-1]-k);}
int main(){
scanf("%d%d",&n,&L);
for(int j=1;j<=n;j++){
scanf("%d",&c[j]);
s[j]=s[j-1]+c[j];
}
pp[0]=1;
for(int j=1;j<=n;j++){
while(l<r && js(pp[l+1],pp[l])<2*(s[j]+j-L)) l++;
dp[j]=dp[pp[l]-1]+(s[j]-s[pp[l]-1]+j-pp[l]-L)*(s[j]-s[pp[l]-1]+j-pp[l]-L);
while(l<r && js(pp[r],pp[r-1])>js(j+1,pp[r])) r--;
pp[++r]=j+1;
}
printf("%lld",dp[n]);
return 0;
}