bzoj 1010 [HNOI2008] 玩具装箱toy (斜率优化第一题)

题意:中文题,大意就不说了,但这个Sigma的确让我看了好久

思路:看了网上的很多题解,大概对斜率优化有了一点了解,发现斜率优化这种东西用在很多方面,比如在对凸壳的求解中,常用的kuangbin凸包模板中就有斜率优化的例子,对于单调队列的认识是基于某次被学弟们踩的一次训练(bzoj 1012 [JSOI2008] 最大数maxnumber,这个题用点掉队列的解法的确很巧妙,orz.),而斜率优化,与点掉队列优化类似,队列优化解决的是在dp方程中,经过一些变化后,可以将dp方程两侧化为dp[i]和dp[j],而无dp[i]*dp[j]类似的项,就是不牵扯高次幂项,而写斜率优化正是将相乘的两项转化为一种特殊的函数,转变成在二维平面上的一些点,在决策性的讨论上,根据所变形得到的方程,来决定维护上凸壳还是下凸壳。

首先附上超时代码O(n2);

加个字符读入板子:

inline char nc() {
    static char ibuf[RLEN],*ib,*ob;
    (ib==ob) && (ob=(ib=ibuf)+fread(ibuf,1,RLEN,stdin));
    return (ib==ob) ? -1 : *ib++;
}

 

#include <bits/stdc++.h>
using namespace std;
const int maxn=50005;
const int INF=0x3f3f3f3f;
typedef long long LL;
LL pre[maxn],dp[maxn];
int a[maxn];
int n,l;

int main()
{
    scanf("%d%d",&n,&l);
    pre[0]=0;
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        pre[i]=pre[i-1]+a[i];
    }
    memset(dp,0x3f,sizeof(dp));
    dp[0]=0;
    for(int i=1;i<=n;i++){
        int j;
        int as;
        for(j=0;j<i;j++){
            as=i-j-1+pre[i]-pre[j]-l;
            dp[i]=min(dp[i],dp[j]+as*as);
        }
    }
    printf("%lld\n",dp[n]);
    return 0;
}

然后抄袭hzw大牛的代码:

#include <bits/stdc++.h>
const int inf=1000000000;
typedef long long ll;
using namespace std;
ll read()
{
    ll x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int n,L,l,r;
int c[50005],q[50005];
ll s[50005],f[50005],C;
double slop(int j,int k)
{
    return (f[k]-f[j]+(s[k]+C)*(s[k]+C)-(s[j]+C)*(s[j]+C))/(2.0*(s[k]-s[j]));
}
int main()
{
    n=read();L=read();C=L+1;
    for(int i=1;i<=n;i++)c[i]=read();
    for(int i=1;i<=n;i++)s[i]=s[i-1]+c[i];
    for(int i=1;i<=n;i++)s[i]+=i;//这里是因为在dp方程中吧前缀和与i进行合并了
    l=1;r=0;q[++r]=0;
    for(int i=1;i<=n;i++){
        while(l<r&&slop(q[l],q[l+1])<=s[i])l++;
        int t=q[l];//取出队首
        f[i]=f[t]+(s[i]-s[t]-C)*(s[i]-s[t]-C);//这里其实是dp方程
        while(l<r&&slop(q[r],i)<slop(q[r-1],q[r]))r--;//由斜率方程可以得出,维护的是下凸壳,所以当队列存在且
        q[++r]=i;                   //加入决策i时,令队尾为q[r],前一个为q[r-1]
                            //满足斜率(q[r],i)<斜率(q[r-1],q[r])时,显然队尾是无效的,将其弹出
    }
    printf("%lld\n",f[n]);
    return 0;
}

 

posted @ 2018-02-27 10:29  啦啦啦天啦噜  阅读(181)  评论(0编辑  收藏  举报