zoukankan      html  css  js  c++  java
  • TJOI2011书架(dp)

    当农夫约翰闲的没事干的时候,他喜欢坐下来看书。多年过去,他已经收集了 N 本书 (1 <= N <= 100,000), 他想造一个新的书架来装所有书。

    每本书 i 都有宽度 W(i) 和高度 H(i)。书需要按顺序添加到一组书架上;比如说,第一层架子应该包含书籍1 ... k,第二层架子应该以第k + 1本书开始,以下如此。每层架子的总宽度最大为L(1≤L≤1,000,000,000)。每层的高度等于该层上最高的书的高度,并且整个书架的高度是所有层的高度的总和,因为它们都垂直堆叠。

    请帮助农夫约翰计算整个书架的最小可能高度。

    有N(1 <= N <= 100000)本书,每本书有一个宽度W(i),高度H(i),(1 <= H(i) <= 1,000,000; 1 <= W(i) <= L)。

    现在有足够多的书架,书架宽度最多是L (1 <= L <= 1,000,000,000),把书按顺序(先放1,再放2.....)放入书架。某个书架的高度是该书架中所放的最高的书的高度。

    将所有书放入书架后,求所有书架的高度和的最小值?

    Solution

    一眼dp优化。

    状态方程显然,dp[[i]=dp[j]+maxh(j~i).然后用线段树优化,区间取max,单点修改。

    但答案是两部分构成的,区间取max会炸。。

    于是我想了一晚上。。。

    我们发现每次取max会改变一段连续的区间,所以我们维护一个单调递减的队列,每次找到前面第一个比它大的点,直接区间修改就可以了。

    Code

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #define N 100002
    using namespace std;
    typedef long long ll;
    long long tr[N<<2],sum[N],w[N],m,n,h[N],x,q[N],la[N<<2],ans[N<<2];
    inline void pushdown(int cnt){
        la[cnt<<1]=la[cnt<<1|1]=la[cnt];la[cnt]=0; 
        ans[cnt<<1]=tr[cnt<<1]+la[cnt<<1];ans[cnt<<1|1]=tr[cnt<<1|1]+la[cnt<<1|1];
    }
    void add(int cnt,int l,int r,int x,ll y){
        if(l==r){
            tr[cnt]=y;
            ans[cnt]=y+la[cnt];
            return;
        }
        int mid=(l+r)>>1;
        if(la[cnt])pushdown(cnt);
        if(mid>=x)add(cnt<<1,l,mid,x,y);
        else add(cnt<<1|1,mid+1,r,x,y);
        tr[cnt]=min(tr[cnt<<1],tr[cnt<<1|1]);
        ans[cnt]=min(ans[cnt<<1],ans[cnt<<1|1]);
    }
    ll query(int cnt,int l,int r,int L,int R){ 
        if(l>=L&&r<=R)return ans[cnt];
        int mid=(l+r)>>1;
        ll ans=1e18;
        if(la[cnt])pushdown(cnt);
        if(mid>=L)ans=min(ans,query(cnt<<1,l,mid,L,R));
        if(mid<R)ans=min(ans,query(cnt<<1|1,mid+1,r,L,R));
        return ans;
    }
    void upd(int cnt,int l,int r,int L,int R,ll x){
        if(L>R)return;
        if(l>=L&&r<=R){
            la[cnt]=x;
            ans[cnt]=tr[cnt]+la[cnt];
            return;
        } 
        int mid=(l+r)>>1;
        if(la[cnt])pushdown(cnt);
        if(mid>=L)upd(cnt<<1,l,mid,L,R,x);
        if(mid<R)upd(cnt<<1|1,mid+1,r,L,R,x);
        tr[cnt]=min(tr[cnt<<1],tr[cnt<<1|1]);
        ans[cnt]=min(ans[cnt<<1],ans[cnt<<1|1]);
    }
    int find(int x){
        int l=0,r=x,ans;
        while(l<=r){
            int mid=(l+r)>>1;
            if(sum[x]-sum[mid]<=m){
                ans=mid;
                r=mid-1;
            } 
            else l=mid+1;
        }
        return ans;
    } 
    int main(){
        scanf("%lld%lld",&n,&m);
        memset(tr,0x3f,sizeof(tr));memset(ans,0x3f,sizeof(ans));
        for(int i=1;i<=n;++i)scanf("%lld%lld",&h[i],&w[i]),sum[i]=sum[i-1]+w[i];
        int mn=0;
        add(1,0,n,0,0);
        int hh,t;
        hh=t=1;q[hh]=0;
        for(int i=1;i<=n;++i){
            while(hh<=t&&h[q[t]]<h[i])t--;
            upd(1,0,n,hh<=t?q[t]:0,i-1,h[i]);
            q[++t]=i;
            x=query(1,0,n,find(i),i-1);
            add(1,0,n,i,x);
        }
        printf("%lld",x);
        return 0;
    }
  • 相关阅读:
    ASP设计常见问题及解答精要
    网页脚本加密解密
    有关表格边框的css样式表语法说明
    彻底搞定 Grub
    三千年来振奋过中国人的29句口号(是中国人就看看!)
    在Unix/Linux上令(java)JVM支持中文输出
    windows xp 下eclipse3.0.2+eclipseme+j2me wireless tooltik开发环境的配置
    在网页上显示公式
    Oracle认证考试详细介绍
    算法和数据结构排序快速排序
  • 原文地址:https://www.cnblogs.com/ZH-comld/p/9723865.html
Copyright © 2011-2022 走看看