书架_纪中2931_dp_堆_单调队列

题目大意

  当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计算书架可能的最小总高度。


分析

  一眼就看出是  dp (因为每天的第四或第三题都是dp)

  最简单的方程是

 f[i]=min{f[j]+max(h[j],h[j+1]...h[i])}

  其中w[j]+w[j+1]...w[i]<=l。

  但是这个东西是O(n²),会超时。所以联系到求最大值,可以想到用最大值的单调队列,队列里面存放的是以 i 为结尾,窗口里面的元素和恰好不大于 M 的 j 为左边界,即恰好有 sum[i] - sum[j-1] <= M.同时h也用单调队列优化。

  然后这样一个可行解就是,f[ i ]=f[j]+h[a[r]](即队首元素的值), 这并不是最优解,所以还要找到队列中的最优解,一个可能的最优解只能是这样的f[i]=f[a[r-1]]+h[a[r]],也就是 a[ r ] 要大于后面的数,很显然,如果a[ r]小于后面的数,那么我们就可以将 a[ r ] 划分到后面去,而取得更优解。这里涉及的这个找最优解问题,可以用set。

  看不懂的自行脑补(虽然我也不是很知道我在打什么,具体的见代码)

  又是他——                蜜汁c++

代码

#include <stdio.h>  
#include <iostream>
#include <sstream>  
#include <set>
using namespace std;  
typedef long long ll;  
ll que[100100],a[100100],dp[100100],b[100100]; 
ll n,m,sum;
ll i,j,k;
ll front,rear,p=1;
multiset <ll> lxf; //erase insert begin  
int main()  
{      
  scanf("%lld%lld",&n,&m);   
  for(i=1;i<=n;i++){
    scanf("%lld%lld",&a[i],&b[i]);
  }
  front=1; rear=1;
  dp[1]=a[1];que[rear]=1;  
  sum=b[1];  
  for(i=2;i<=n;i++){  
    sum+=b[i];  
    while(sum>m)
      sum-=b[p++];
    while( front<=rear&&a[i]>=a[que[rear]]){
      lxf.erase(dp[que[rear-1]]+a[que[rear]]);
      rear--;
    }
    rear=rear+1;
    que[rear]=i;
    if (front<rear) {
      lxf.insert(dp[que[rear-1]]+a[que[rear]]);
    }
    while(que[front]<p&&front<=rear){
      lxf.erase(dp[que[front]]+a[que[front+1]]);
      front++;
    }
    dp[i]=dp[p-1]+a[que[front]];  
    j=*lxf.begin();
    if ((j<dp[i])&&(j!=0)) dp[i]=j;          
  }  
  cout<<dp[n]<<endl;  
  return 0;  
}  

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值