zoukankan      html  css  js  c++  java
  • 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
    ",dp[n]);
    47     return 0;
    48 }
  • 相关阅读:
    [LeetCode] Kth Smallest Element in a BST
    Dojo入门篇
    不要小看了get 与set
    怎样安装Windows7操作系统
    MFC Wizard创建的空应用程序中各个文件内容的解析
    hadoop hdfs空间满后重新启动不了
    树形结构——基本原理
    R语言pdf输出中文乱码处理
    Javascript基本概念梳理
    Java动态代理
  • 原文地址:https://www.cnblogs.com/fu3638/p/7807068.html
Copyright © 2011-2022 走看看