zoukankan      html  css  js  c++  java
  • [BZOJ] 1010 [HNOI2008]玩具装箱toy

    Time Limit: 1 Sec  Memory Limit: 162 MB
    Submit: 12664  Solved: 5539
    [Submit][Status][Discuss]
    Description
      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。但他希望费用最小.
    
    Input
      第一行输入两个整数NL.接下来N行输入Ci.1<=N<=50000,1<=L,Ci<=10^7
    
    Output
      输出最小费用
    
    Sample Input
    5 4
    
    3
    
    4
    
    2
    
    1
    
    4
    Sample Output
    1
    HINT
    Source
    
    [Submit][Status][Discuss]

    斜率优化第一题,感觉很玄妙。
    dp(i)=min(dp(i)+(sum(i)-sum(j)+i-j-1-L)^2)
    设a(i)=sum(i)+i,b(i)=sum(i)+i+1+L
    可见a,b均单增
    接着原式可以化为
    dp(i)= dp(j)+(a(i)-b(j))^2
    2a(i)b(j)+dp(i)-a(i)^2=dp(j)+b(j)^2
    设X=b(j),Y=dp(j)+b(j)^2
    Y=2a(i)X+dp(i)-a(i)^2
    这是一条斜率为2a(i),截距为dp(i)-a(i)^2的直线
    对于一个i,a(i)是定值,要最小化dp(i),就要最小化截距

    这个直线过点(b(j),dp(j)+b(j)^2),也就是刚才代换的X和Y
    b(j)单增,所以这些点横坐标单增,可以用单调队列维护一个凸包

    一个点P是最优解当且仅当slope(P,P+1)>=2a(i),这时候就可以与直线相切了。

    维护的时候,在队首删掉斜率小于2a(i)的点,因为随着a(i)单增,这些点一定不可能成为最优了
    在队尾删掉斜率大于slope(Pi,Ptail)的点,否则就够不成凸包了。

    有几个不理解的地方,为什么公式里的min可以去掉?这样推导的动力是什么?

    #include<iostream>
    #include<cstdio>
    #define int long long
    using namespace std;
    
    const int MAXN=500005;
    
    inline int rd(){
        int ret=0,f=1;char c;
        while(c=getchar(),!isdigit(c))f=c=='-'?-1:1;
        while(isdigit(c))ret=ret*10+c-'0',c=getchar();
        return ret*f;
    }
    
    int n,L;
    int dp[MAXN],sum[MAXN];
    
    inline double a(int x){return sum[x]+x;}
    inline double b(int x){return sum[x]+x+1+L;}
    inline double X(int x){return b(x);}
    inline double Y(int x){return dp[x]+b(x)*b(x);}
    inline double slope(int x,int y){return 1.0*(Y(y)-Y(x))/(X(y)-X(x));}
    
    int Q[MAXN],head=1,tail=1;
    
    signed main(){
        n=rd();L=rd();
        for(int i=1;i<=n;i++){
            int x=rd();
            sum[i]=sum[i-1]+x;
        }
        for(int i=1;i<=n;i++){
            while(head<tail&&slope(Q[head],Q[head+1])<2*a(i)) head++;
            dp[i]=dp[Q[head]]+(a(i)-b(Q[head]))*(a(i)-b(Q[head]));
            while(head<tail&&slope(i,Q[tail-1])<slope(Q[tail-1],Q[tail])) tail--;
            Q[++tail]=i;
        }
        cout<<dp[n]<<endl;
        return 0;
    }

    本文来自博客园,作者:GhostCai,转载请注明原文链接:https://www.cnblogs.com/ghostcai/p/9247405.html

  • 相关阅读:
    strcpy,memset,memcpy三者之间的根本区别
    最便捷、最强大、速度最快的C++序列化框架
    C++读写二进制文件
    boost binary 序列化
    febird.dataio和boost.serialization性能对比
    Boost文本序列化和二进制序列化的效率比较
    Boost文本序列化和二进制序列化的效率比较
    c++的vector赋值方法汇总
    OCP-1Z0-051-V9.02-36题
    遍历list或map时删除元素(较巧妙)
  • 原文地址:https://www.cnblogs.com/ghostcai/p/9247405.html
Copyright © 2011-2022 走看看