zoukankan      html  css  js  c++  java
  • 书架 bookshelf

    书架 bookshelf

    题目描述

        当Farmer John闲下来的时候,他喜欢坐下来读一本好书。 多年来,他已经收集了N本书 (1 <= N <= 100,000)。 他想要建立一个多层书架,来存放它们。

         每本书 i 拥有一个宽度 W(i)和一个高度 H(i)。 所有的书需要按顺序,放到书架的每一层。 举例来说,第一层书架放k本书,应该放书1...k;第二层书架从第k+1本书开始放……。 每层书架的宽度最多为L (1 <= L <= 1,000,000,000)。 每层书架的高度为该层最高的那本书的高度。 书架的总高度为每层书架高度的和。

         请帮FJ计算书架可能的最小总高度。

     

    输入

    第1行:两个空格隔开的整数:N和L 
    第2行..第N+1行:第i+1行包含两个空格隔开的整数:H(i) 和 W(i) 。(1 <= H(i) <= 1,000,000; 1 <= W(i) <= L)。

    输出

    仅一行,表示书架可能的最小总高度

    样例输入

    5 10
    5 7
    9 2
    8 5
    13 2
    3 8
    

    样例输出

    21
    

    提示

    【样例解释】

    一共有5本书。每层书架的宽度最多为10。

    3层书架,第1层放书1(高5,宽7),第2层放书2..4(高13,宽9),第3层放书5(高3,宽8)。

    【数据范围】

    45%的数据:1<=N<=5,000

    100%的数据:1<=N<=100,000,1<=Wi<=L<=1,000,000,000,1<=Hi<=1,000,000


    solution

    考虑暴力dp

    f[i]表示放好前i本书的最小高度。

    f[i]=min (f[j]+max(h[j+1]--h[i])) (w[i]-w[j]<=L)

    效率O(n^2)

    现在优化时间复杂度。

    枚举右端点r,我们维护最靠前的可转移位置l。

    也就是我要知道[l,r]内f[i]+maxh[i~r]的最小值

    由于f不变,h可能变(而且是整段改变),我们用线段树维护。

    f相当于单点加,而h则是区间修改。

    分别维护f的最小值和f+h的最小值,改变h时用旧的f与新的h更新f+h即可。

    #include<cstdio>
    #include<iostream>
    #include<cstdlib>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #define maxn 100005
    #define inf 1e16
    #define ll long long
    using namespace std;
    int n,id[maxn],top;
    ll w[maxn],h[maxn],zh[maxn],len,fn;
    struct node{
        ll f,bj,mi;
    }tr[maxn*4];
    void update(int k,ll v){
        tr[k].bj=v;tr[k].mi=tr[k].f+v;
    }
    void down(int k){
        if(tr[k].bj){
            ll &v=tr[k].bj;
            update(k*2,v);update(k*2+1,v);
            v=0;
        }
    }
    void wh(int k){
        tr[k].f=min(tr[k*2].f,tr[k*2+1].f);
        tr[k].mi=min(tr[k*2].mi,tr[k*2+1].mi);
    }
    void build(int k,int l,int r){
        if(l==r){
            if(l) tr[k].f=tr[k].mi=inf;
            return;
        }
        int mid=l+r>>1;
        build(k*2,l,mid);build(k*2+1,mid+1,r);
        wh(k);
    }
    void ch(int k,int l,int r,int li,int ri,ll v){
        if(l>=li&&r<=ri){
            update(k,v);return;
        }
        down(k);
        int mid=l+r>>1;
        if(li<=mid)ch(k*2,l,mid,li,ri,v);
        if(ri>mid)ch(k*2+1,mid+1,r,li,ri,v);
        wh(k);
    }
    ll ask(int k,int l,int r,int li,int ri){
        if(l>=li&&r<=ri){
            return tr[k].mi;
        }
        down(k);
        int mid=l+r>>1;
        ll tmp=inf;
        if(li<=mid)tmp=min(tmp,ask(k*2,l,mid,li,ri));
        if(ri>mid)tmp=min(tmp,ask(k*2+1,mid+1,r,li,ri));
        return tmp;
    }
    void add(int k,int l,int r,int pl,ll v){
        if(l==r){
             
            tr[k].f=v;return;
        }
        down(k);
        int mid=l+r>>1;
        if(pl<=mid)add(k*2,l,mid,pl,v);
        else add(k*2+1,mid+1,r,pl,v);
        wh(k);
    }
    int main(){
         
        cin>>n>>len;
        for(int i=1;i<=n;i++){
            scanf("%lld%lld",&h[i],&w[i]);
            w[i]+=w[i-1];
        }
        int la=0;
        build(1,0,n);
        for(int i=1;i<=n;i++){
            while(top&&h[i]>zh[top])top--;
            //cout<<"ch "<<id[top]<<' '<<i-1<<endl;
            ch(1,0,n,id[top],i-1,h[i]);
            while(w[i]-w[la]>len)la++;
            //cout<<"la "<<la<<endl;
            fn=ask(1,0,n,la,i-1);
            //cout<<fn<<endl;
            add(1,0,n,i,fn);
            zh[++top]=h[i];id[top]=i;
        }
        cout<<fn<<endl;
        return 0;
    }
     
    View Code
  • 相关阅读:
    【动态规划/二维背包问题】mr355-三角形牧场
    【动态规划】mr354-坐车看球
    【深度优先搜索】mr353-取奶
    【动态规划】mr351-办签证
    【贪心】POJ2393-Yogurt Factory
    centos 7 systemctl
    linux 程序或服务开机自启动
    linux终端快捷键
    linux 安装
    unix
  • 原文地址:https://www.cnblogs.com/liankewei/p/10384179.html
Copyright © 2011-2022 走看看