BZOJ 1010 玩具装箱toy(斜率优化DP)

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1010

题目大意:P教授要去看奥运,但是他舍不下他的玩具,于是他决定把所有的玩具运到北京。他使用自己的压缩器进行压
缩,其可以将任意物品变成一堆,再放到一种特殊的一维容器中。P教授有编号为1...N的N件玩具,第i件玩具经过
压缩后变成一维长度为Ci.为了方便整理,P教授要求在一个一维容器中的玩具编号是连续的。同时如果一个一维容
器中有多个玩具,那么两件玩具之间要加入一个单位长度的填充物,形式地说如果将第i件玩具到第j个玩具放到一
个容器中,那么容器的长度将为 x=j-i+Sigma(Ck) i<=K<=j 制作容器的费用与容器的长度有关,根据教授研究,
如果容器长度为x,其制作费用为(X-L)^2.其中L是一个常量。P教授不关心容器的数目,他可以制作出任意长度的容
器,甚至超过L。但他希望费用最小。

解题思路:我们可以很容易得到O(n^2)的状态转移方程:dp[i]=min{dp[j]+(sum[i]-sum[j]+i-j-1-l)^2}。显然这种写法会超时, 根据状态转移方程的性质,我们可以利用斜率优化,利用队列维护单调性,使得复杂度降为O(n)。

假设存在k<j<i,且j比k优,于是可以写出不等式dp[j]+(sum[i]-sum[j]+i-j-1-l)^2<=dp[k]+(sum[i]-sum[j]+i-j-1-l)^2

设s[j]=sum[j]+j,s[k]=sum[k]+k,C=l+1.

则原式可化为:dp[j]+(s[i]-s[j]-C)^2<=dp[k]+(s[i]-s[k]-C)^2

       dp[j]+(s[j]+C)^2-2*s[i]*(s[j]+C)<=dp[k]+(s[k]+C)^2-2*s[i]*(s[k]+C)

       (dp[j]+(s[j]+C)^2-dp[k]-(s[k]+C)^2)/(2*s[j]-2*s[k])<=s[i]

设yj=dp[j]+(s[j]+C)^2,yk=dp[k]+(s[k]+C)^2,xj=2*s[j],xk=2*s[k]

令g(k,j)=yj-yk/xj-xk.

当①g(k,j)<=s[i],j比k优,k可抛弃。

 ②如果g(k,j)>=g(j,i),那么j点便永远不可能成为最优解,可以直接将它踢出我们的最优解集。

  1)若g(j,i)<=s[i],那么就是说i点要比j点优,排除j点。

  2)如果g(j,i)>s[i],那么j点此时是比i点要更优,但是同时g(k,j)>=g(i,j)>s[i]。这说明还有k点会比j点更优,同样排除j点。

本质:维护一个斜率单调的队列。

代码:

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 using namespace std;
 6 typedef long long LL;
 7 const int N=5e4+5;
 8 
 9 LL l,C;
10 LL sum[N],dp[N],q[N];
11 
12 //yj-yk/xj-xk
13 double Slope(int k,int j){
14     return double(dp[j]+(sum[j]+C)*(sum[j]+C)-dp[k]-(sum[k]+C)*(sum[k]+C))/(2*sum[j]-2*sum[k]);
15 }
16 
17 //dp[i]=min{dp[j]+(sum[i]-sum[j]+i-j-1-l)^2}
18 LL getDP(int i,int j){
19     return dp[j]+(sum[i]-sum[j]-C)*(sum[i]-sum[j]-C);
20 }
21 
22 int main(){
23     int n;
24     scanf("%d%lld",&n,&l);
25     C=l+1;
26     for(int i=1;i<=n;i++){
27         scanf("%lld",&sum[i]);
28         sum[i]+=sum[i-1];
29     }
30     for(int i=1;i<=n;i++){
31         sum[i]+=i;
32     }
33     dp[0]=0;
34     int head=0,tail=0;
35     q[tail++]=0;
36     for(int i=1;i<=n;i++){
37         while(head+1<tail&&Slope(q[head],q[head+1])<=sum[i]){
38             head++;
39         }
40         dp[i]=getDP(i,q[head]);
41         while(head+1<tail&&Slope(q[tail-1],i)<=Slope(q[tail-2],q[tail-1])){
42             tail--;
43         }
44         q[tail++]=i;
45     }
46     printf("%lld\n",dp[n]);
47     return 0;
48 }

 

转载于:https://www.cnblogs.com/fu3638/p/7807068.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值