USACO OPEN 12 BOOKSELF(转)

原文出处:http://txhwind.blog.163.com/blog/static/2035241792012112285146817/(版权为原作者所有,本博文无营利目的,不构成侵权)

题目大意:(POJ 3017 也是这个)

n本书有各自的高度h_i和宽度w_i. 现在要把它分成若干段,要求每段总宽度不超过l,某段的高度为其中书的高度的最大值。求最小的所有段的高度和。

简单题解:

预处理w的前缀和s.

首先得到动规方程:f[i]=min{f[j]+max{h[j+1..i]} | s[i]-s[j]<=l}.

直接做是平方的,如何优化呢?

我们发现f[j]不减,且对某个i,max{h[j+1..i]}不增。

考虑维护hmax值相同的段。那么从i-1到i时,用h[i]把前面hmax比它小的段合并掉。

平衡树维护每段最前面的f值,决策时取最小的。

翱犇思路:用线段树维护所有决策,每次将一段的hmax修改为h[i].

 1 #include<stdio.h>
 2 #include<set>
 3
 4 using std::multiset;
 5
 6 const int MAXN=100000+9;
 7
 8 int h[MAXN],s[MAXN],w[MAXN];
 9 long long f[MAXN];
10 int main()
11 {
12         freopen("bookshelf.in","r",stdin);
13         freopen("bookshelf.out","w",stdout);
14         int n,l,i;
15         int sum=0,head=1,tail=0;
16         multiset<long long> val;
17         scanf("%d%d",&n,&l);
18         for(i=1;i<=n;++i)
19         {
20                 scanf("%d%d",h+i,w+i);
21
22                 //s记录每段开头
23                 for(s[++tail]=i-1;head<tail && h[i]>=h[s[tail]];--tail)
24                         val.erase(val.find(f[s[tail-1]]+h[s[tail]]));
25                 val.insert(f[s[tail]]+h[i]);
26                 s[tail+1]=i;
27                 for(sum+=w[i];sum>l;sum-=w[s[head]])
28                 {
29                         val.erase(val.find(f[s[head]]+h[s[head+1]]));
30                         if(++s[head]==s[head+1])
31                                 ++head;
32                         else val.insert(f[s[head]]+h[s[head+1]]);
33                 }
34                 f[i]=*val.begin();
35         }
36         printf("%lld\n",f[n]);
37         return 0;
38 }

以下是我的同学的解题报告:不涉及任何侵权问题。。同学的权,随便侵。。。。

这道题的原型是POJ的3017,只是W[I]换成数列中的权值而已;
对题目进行分析,不难得到动规方程f[i]=min(f[j]+max(h[j+1],h[j+2],h[j+3]....h[i]))其中w[j+1]+w[j+2]+...w[j]<=l;

但是这只是O(n^2)的做法,必须想出优化办法;

首先我们会发现一个i状态会与许多j的子状态相关,这种情况大多都是与单调队列的优化相关;但是这道题并不这么明显;

首先,我们向求max的h[j]值方向思考,不难维护出一个单调递减,队头到i的w[i]之和小于l的最大值单调队列;但是这样就将f的值给忽略掉了;

所以我们需要想出f数组的特点,不难看出这是单调递增的,

所以我们猜想:每一个单调队列中的决策点相对于后面的一段都有可能是最优决策点,但是如何证明呢?

如果这个不是最优决策点,那么他后面的一段区间的最大值都与该点相等,而f非递增,所以决策都没有在单调队列中的决策好;

既然我们发现f与单调队列也有关系,那么此题单调队列的方向就已经确定;

维护上述的一个单调队列,在计算f[i]时在单调队列中的所有点都有可能成为决策点,对于这种情况可以用平衡树进行维护,但代码还有许多细节处理

 1  #include<cstdio>
 2     #include<set>
 3     #include<iostream>
 4     using namespace std;
 5     const int mm=111111;
 6     int a[mm],q[mm],w[mm];
 7     long long f[mm],sum,tmp,m;
 8     multiset<long long> sbt;
 9     int i,l,r,p,n;
10     int main()
11     {
12         scanf("%d%lld",&n,&m);
13         sbt.clear();
14         sum=l=0,f[n]=r=-1;
15         for(p=i=1;i<=n;++i)
16         {
17             scanf("%d%d",&a[i],&w[i]);
18             sum+=w[i];
19             while(sum>m)sum-=w[p++];
20             while(l<=r&&a[i]>=a[q[r]])
21             {
22                 if(l<r)sbt.erase(f[q[r-1]]+a[q[r]]);
23                 --r;
24             }
25             q[++r]=i;
26             if(l<r)sbt.insert(f[q[r-1]]+a[q[r]]);  //手动模拟下就会发现对于边界外决策点,其待定决策值为f[q[i]-1]+a[q[i]];
27             while(q[l]<p)
28             {
29                 if(l<r)sbt.erase(f[q[l]]+a[q[l+1]]);
30                 ++l;
31             }
32             f[i]=f[p-1]+a[q[l]];  //在i为当前最大值时用p数组更新
33
34             tmp=*sbt.begin();
35             if(f[i]>tmp)f[i]=tmp;
36         }
37         cout<<f[n];
38         return 0;
39     }  

时间: 2024-10-11 11:22:26

USACO OPEN 12 BOOKSELF(转)的相关文章

vc编程中的20点小笔记

机器学习是一项经验技能,经验越多越好.在项目建立的过程中,实践是掌握机器学习的最佳手段.在实践过程中,通过实际操作加深对分类和回归问题的每一个步骤的理解,达到学习机器学习的目的. 预测模型项目模板不能只通过阅读来掌握机器学习的技能,需要进行大量的练习.本文将介绍一个通用的机器学习的项目模板,创建这个模板总共有六个步骤.通过本文将学到: 端到端地预测(分类与回归)模型的项目结构. 如何将前面学到的内容引入到项目中. 如何通过这个项目模板来得到一个高准确度的模板. 副诼匚盼胁臼匾膊讶赖期放判鼻懒合谖

USACO银组12月月赛题解

USACO银组12月月赛题解 Convention 题面 一场别开生面的牛吃草大会就要在Farmer John的农场举办了! 世界各地的奶牛将会到达当地的机场,前来参会并且吃草.具体地说,有N头奶牛到达了机场(1≤N≤105),其中奶牛i在时间ti(0≤ti≤109)到达.Farmer John安排了M(1≤M≤105)辆大巴来机场接这些奶牛.每辆大巴可以乘坐C头奶牛(1≤C≤N).Farmer John正在机场等待奶牛们到来,并且准备安排到达的奶牛们乘坐大巴.当最后一头乘坐某辆大巴的奶牛到达的

【USACO 1.3.4】牛式

[題目描述 ] 下面是一个乘法竖式,如果用我们给定的那n个数字来取代*,可以使式子成立的话,我们就叫这个式子牛式. * * * x * * ---------- * * * * * * ---------- * * * * 数字只能取代*,当然第一位不能为0,况且给定的数字里不包括0. 注意一下在美国的学校中教的"部分乘积",第一部分乘积是第二个数的个位和第一个数的积,第二部分乘积是第二个数的十位和第一个数的乘积. 写一个程序找出所有的牛式. [格式] INPUT FORMAT: (f

USACO Chapter 1 Section 1.1

USACO的题解和翻译已经很多了... 我只是把自己刷的代码保存一下. 1.PROB Your Ride Is Here 1 /* 2 ID:xiekeyi1 3 PROG:ride 4 LANG:C++ 5 */ 6 7 #include<bits/stdc++.h> 8 using namespace std ; 9 10 int main() 11 { 12 freopen("ride.in","r",stdin); 13 freopen(&quo

usaco月赛,2017.1总结

T1:跳舞的奶牛 大致题意:一个体积为k的舞台能够同时容纳k只奶牛一起跳舞,他们每头奶牛的跳舞时间不同,如果有一只奶牛跳完了第k+1头奶牛就会立刻上场跳舞,当所有奶牛跳完舞以后我们认为这次表演结束.现在给出奶牛个数,最多用时,每头奶牛的跳舞时间.求舞台最小为多大. 思路:本来写了个程序以为这道题很简单,刚开始排一下序然后就行了,结果交了以后发现只过了五组,然后才发现这道题不能改变顺序(所以说为什么我改变顺序了还是能过五组,usaco的数据也好水......),所以说我想到了堆,然后就用堆写了一下

插入排序的优化【不靠谱地讲可以优化到O(nlogn)】 USACO 丑数

首先我们先介绍一下普通的插排,就是我们现在一般写的那种,效率是O(n^2)的. 普通的插排基于的思想就是找位置,然后插入进去,其他在它后面的元素全部后移,下面是普通插排的代码: 1 #include<iostream> 2 #include<fstream> 3 #include<stdio.h> 4 using namespace std; 5 int a[200000]; 6 int p[200000]; 7 8 int main(){ 9 ios::sync_wi

USACO抓牛catchcow (bfs)

这题是黄巨大出的比赛题. http://poj.org/problem?id=3278 Description Farmer John has been informed of the location of a fugitive cow and wants to catch her immediately. He starts at a point N (0 ≤ N ≤ 100,000) on a number line and the cow is at a point K (0 ≤ K ≤

【USACO 1.2.2】方块转换

[问题描述] 一块N x N(1<=N<=10)正方形的黑白瓦片的图案要被转换成新的正方形图案.写一个程序来找出将原始图案按照以下列转换方法转换成新图案的最小方式: 1:转90度:图案按顺时针转90度. 2:转180度:图案按顺时针转180度. 3:转270度:图案按顺时针转270度. 4:反射:图案在水平方向翻转(以中央铅垂线为中心形成原图案的镜像). 5:组合:图案在水平方向翻转,然后再按照1到3之间的一种再次转换. 6:不改变:原图案不改变. 7:无效转换:无法用以上方法得到新图案. 如

USACO 6.5 Closed Fences

Closed Fences A closed fence in the plane is a set of non-crossing, connected line segments with N corners (3 < N < 200). The corners or vertices are each distinct and are listed in counter-clockwise order in an array {xi, yi}, i in (1..N). Every pa