NIIFL的博客

NIIFL的博客

[HNOI2008]玩具装箱TOY_斜率优化学习笔记

题目描述

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。但他希望费用最小。

题目分析

对于此题,我们可以容易列出状态转移方程,我们设sum[i]表示前i个玩具的长度之和,设状态f[i]表示分组完前i个物品所需的最小花费,列出如下状态转移方程。
f[i]=min(f[j]+((sum[i]sum[j]+ij1)L)2)
对于此方程,我们发现是不行的,所以对这个式子进行优化,也就是即将讲的斜率优化

斜率优化

对上边的这个式子我们可以进行变形
为了方便在输入时,我们让L++,那么L=L+1
f[i]=f[j]+((sum[i]+i)+(sum[j]+j)L)2
之后为了简便,我们设s[i]等于sum[i]+i,所以原式变为
f[i]=f[j]+(s[i]+s[j]L)2
把该式子展开得
f[i]=f[j]+s[i]2+(s[j]+L)22s[i](s[j]+L)
移项,整理得
f[i]+2s[i](s[j]+L)=f[j]+s[i]2+(s[j]+L)2
之后我们发现这可以转换成一个直线的方程
b+           kx=                              y

进行到这里我们就可以看出一些规律了
任意的一个j都可以看做一个点(s[j]+L,f[j]+s[i]^2+(s[j]+L)^2),并且这条直线的斜率为2*s[i]
我们最终要求的是f[i],也就是b+kx=y里的b,所以对于所有的j,我们需要找出这样一个点,使得b最小,也就是直线的纵截距最小。
之后,我们将这些点维护一个凸包,通过凸包来进行优化
这里写图片描述

如上图所示,当前的斜率为2*s[i],我们可以发现如果该直线经过B点比起经过A点在y轴上的截距要更小,因为直线AB的斜率比当前斜率要小,在来看当直线过C点时,与过A点时的纵截距是相同的,因为他们的斜率相同。同理,c点,d点肯定不会更优。
因此,我们就可以维护一个单调队列,把斜率比当前小的依次取出,那么剩下的那个点就是最优点。
之后就可以去更新f[i],只要更新完f[i],那么我们就可以把i点加入到我们的单调队列。
那么这个点可不可以进入单调队列呢?
这里写图片描述
我们现在要放的点为i,我们看DE,CD的斜率都要比Ci要大,我们将D,E依次从单调队列中取出,此时就可以把I点插入了。

AC代码

#include<iostream>
#include<cstdio>
using namespace std;
const int MAXN=50005;
typedef long long LL;
LL s[MAXN],f[MAXN],N,L;
LL Q[MAXN],head,tail;
inline double X(LL x){return s[x];}
inline double Y(LL x){return f[x]+(s[x]+L-1)*(s[x]+L-1);}//计算j点的x,y坐标,方便代码书写
inline double Rate(LL x,LL k){return (Y(k)-Y(x))/(X(k)-X(x));}//计算斜率
int main(){
    scanf("%lld%lld",&N,&L);
    s[0]=0; L++; //为方便,将L++,上文有提到
    head=tail=1; Q[1]=0;
    for(int i=1;i<=N;i++){scanf("%lld",&s[i]); s[i]+=s[i-1];}
    for(int i=1;i<=N;i++) s[i]+=i; //用s[i]替换sum[i]+i
    for(int i=1;i<=N;i++){
        while(!(head>=tail)&&Rate(Q[head],Q[head+1])<2*s[i]) head++;//取出斜率比当前大的
        int j=Q[head]; f[i]=f[j]+(s[i]-s[j]-L)*(s[i]-s[j]-L);//更新f[i]
        while(!(head>=tail)&&Rate(Q[tail-1],Q[tail])>Rate(Q[tail],i)) tail--;//更新队尾,为插入i做准备
        Q[++tail]=i;//插入i   
    } 
    printf("%lld\n",f[N]);
    return 0;
}
阅读更多
个人分类: 斜率优化dp
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

不良信息举报

[HNOI2008]玩具装箱TOY_斜率优化学习笔记

最多只允许输入30个字

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭