zoukankan      html  css  js  c++  java
  • usaco 土地并购 && hdu 玩具装箱

    土地并购:

    Description

    约翰准备扩大他的农场,眼前他正在考虑购买N块长方形的土地。如果约翰单买一块土地,价格就是土地的面积。但他可以选择并购一组土地,并购的价格为这些土地中最大的长乘以最大的宽。比如约翰并购一块3 × 5和一块5 × 3的土地,他只需要支付5 × 5 = 25元,比单买合算。

    约翰希望买下所有的土地。他发现,将这些土地分成不同的小组来并购可以节省经费。给定每份土地的尺寸,请你帮助他计算购买所有土地所需的最小费用。

    Input Format

    第一行:一个整数:N,1 ≤ N ≤ 50000

    第二行到第M + 1行:第i + 1行有两个整数,分别代表第i块土地的长度Hi和宽度Wi,1 ≤ Hi,Wi ≤ 106

    Output Format

    第一行:购买所有土地所需的最小费用

    ----------------------------------------------------------------

    预处理:

         将 x 从小到大排序

         显然对于 xj < xi 且 yj<xi 时,这个 j 就是无用

         将这样的点删除后,x 递增 ,y递减

    考虑DP方程:

            状态 :f[i] 买前 i 个土地的最少价值。

            转移 :f[i] = min( f[j-1] + x[i] * y[j] ) (1<= j <= i )    

    考虑优化:

           设存在决策 j ,k 当 j 决策优于 k 决策时 

           也就是 : f[j-1] + x[i] * y[j] < f[k-1]+x[i] * y[k]

           进行一个简单的变化 :

                   f[j-1] - f[k-1] < x[i] * (y[k] - y[j] )

           假设 y[k] - y[j] > 0  ,也就是 k < j

                   (f[j-1] - f[k-1])  /  (y[k] - y[j]) < x[i] 

           设点  J ( -y[j] , f[j-1] ) , K(-y[k] , f[k-1])

           也就是 对于横坐标 J > K 时,如果JK连线的斜率小于 x[i]  时 j 决策优于 k 决策

           对于 J 仅保留在 J 前与 J 构成斜率最大的点即可,

           在维护上述条件时,如果存在 i < j < k 且 斜率 Kij > Kjk  则 j 就不可能是最优决策

           证明 :在 j 成为当前决策时 , k 显然会比 j 决策优 !

           Ps.其实就是维护一个下凸壳- =

           有了上述这个性质,通过二分求答案即可 O( n logn );

           又注意到 x 是 递增的 ,所以可以增加一个弹队头的操作(感谢Lazycal大神指导)

            Dp复杂度为O( n )!

      代码如下:

     1 #include<cstdio>
     2 #include<algorithm>
     3 #include<cstring>
     4 #include<string>
     5 #include<cmath>
     6 #include<queue>
     7 #include<iostream>
     8 #define INF 999999999999
     9 #define LL long long
    10 #define N 1000010
    11 //using namespace std ;
    12 struct P{
    13     LL x,y;
    14     P(const LL b=0,const LL c=0) : x(b),y(c){}  
    15 }map[N],q[N];
    16 bool cmp(const P &a,const P &b){
    17     return a.x<b.x||(a.x==b.x&&a.y<b.y);
    18 }
    19 int top,head,n,next[N];
    20 LL f[N];
    21 inline LL cross(P a, P b,P c){
    22     return (b.x-a.x)*(c.y-a.y)-(c.x-a.x)*(b.y-a.y);
    23 }
    24 void addpoint(P now){
    25     while(top-1>0&&cross(q[top-1],q[top],now)<=0) --top;
    26     q[++top]=now;
    27 }
    28 int main(){
    29     freopen("xx.in","r",stdin);
    30     freopen("xx.out","w",stdout);
    31     scanf("%d",&n);
    32     for(int i=1;i<=n;i++)
    33         scanf("%I64d%I64d",&map[i].x,&map[i].y);
    34     std :: sort(map+1,map+1+n,cmp);
    35     int last=n;
    36     for(int i=n-1;i;i--){
    37         if(map[i].y<=map[last].y) continue ;
    38         next[i]=last;
    39         last=i;
    40     }
    41     int b;
    42     for(int i=1;i<=n;i++)
    43     if(next[i]){ 
    44        b=i; 
    45        break;
    46     }
    47     head=1;
    48     for(int last=0,i=b; i ;last=i,i=next[i]){
    49         addpoint(P(-map[i].y,f[last]));
    50                // 二分做法 -->
    51         /*int l=1,r=top,temp=0;
    52         while(l<=r){
    53             int mid=(l+r)>>1;
    54             if(mid==1||q[mid].y-q[mid-1].y<=map[i].x*(q[mid].x-q[mid-1].x)){
    55                 temp=mid;
    56                 l=mid+1;
    57             } else r=mid-1; 
    58         }
    59         f[i]=q[temp].y+map[i].x*(-q[temp].x);*/
    60                // 最优解->
    61         while(head<top&&q[head+1].y-q[head].y<=map[i].x*(q[head+1].x-q[head].x)) ++head; 
    62          f[i]=q[head].y+map[i].x*(-q[head].x);
    63     }
    64     printf("%I64d",f[n]);
    65 }
    View Code

    -------------------------------------------------------------------------

    玩具装箱:

    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

    第一行输入两个整数N,L.接下来N行输入Ci.1<=N<=50000,1<=L,Ci<=10^7

    Output

    输出最小费用

     ---------------------------------------------------------------------------

     和上题基本一样,也是斜率优化

    预处理 g[i]=∑(ci+1) 

    Dp方程: f[i]=f[j]+(g[i]-g[j]-1-L)^2

    设 g[i]-1-L 为 常数 K

    简单的展开可得:

       f[i]=K^2+f[j]+sum[j]^2-2*K*sum[j]

    当 j 优于 k 时

       K^2+f[j]+sum[j]^2-2*K*sum[j]<K^2+f[k]+sum[k]^2-2*K*sum[k]

    设 y[a]=f[a]+sum[a]^2 , x[a]=sum[j]  化简可得 :

        y[j]-y[k]<2*k(x[j]-x[k])

    假设 j>k 则 x[j]>x[k]

        当 y[j]-y[k]/x[j]-x[k]< 2*k 时 j 优于 k 

    然后就真的和上题一样了 - = 

    Ps. 感谢大胖, 小胖 , lazycal等大神指导

    代码如下:

     1 #include<cstring>
     2 #include<algorithm>
     3 #include<cstdio>
     4 #include<string>
     5 #include<iostream>
     6 #include<queue>
     7 #define INF 99999999
     8 #define LL long long
     9 #define N 100010
    10 //using namespace std;
    11 LL L,tail,head,n,g[N],f[N];
    12 struct P{
    13     LL x,y;
    14     P (const LL X=0,const LL Y=0): x(X),y(Y){}
    15 }q[N];
    16 inline LL cross(P a,P b,P c){
    17     return (b.x-a.x)*(c.y-a.y)-(c.x-a.x)*(b.y-a.y);
    18 }
    19 void addpoint(P now){
    20     while(tail>1&&cross(q[tail-1],q[tail],now)<=0) --tail;
    21     head=std::min(head,tail);
    22     q[++tail]=now;
    23 }
    24 int main(){
    25     scanf("%lld%lld",&n,&L);
    26     for(LL l,i=1;i<=n;i++){
    27         scanf("%lld",&l);
    28         g[i]=g[i-1]+l+1;
    29     }
    30     head=1;
    31     tail=0;
    32     for(LL k,i=1;i<=n;i++){
    33         addpoint(P(g[i-1],f[i-1]+g[i-1]*g[i-1]));
    34         k=g[i]-1-L;
    35         while(head<tail&&q[head+1].y-q[head].y<=2*k*(q[head+1].x-q[head].x)) ++head;
    36          f[i]=q[head].y-2*k*q[head].x+k*k;
    37     }
    38     printf("%lld",f[n]);
    39 }
    View Code
  • 相关阅读:
    eclipsesvn
    js邮箱和正则表达式
    jsreplace
    JQuery与Json转换
    thinkPHP时间戳格式化
    JS绝对定位到右下角
    chrome快捷键
    js配置示例
    JQuery class选择器
    JS调试技巧
  • 原文地址:https://www.cnblogs.com/Blacko/p/3388481.html
Copyright © 2011-2022 走看看