单调队列练习之广告印刷

单调队列之广告印刷

【问题描述】
最近,afy决定给TOJ印刷广告,广告牌是刷在城市的建筑物上的,城市里有紧靠着的N个建筑。
afy决定在上面找一块尽可能大的矩形放置广告牌。我们假设每个建筑物都有一个高度,
从左到右给出每个建筑物的高度H1,H2…HN,且0<Hi<=1,000,000,000,并且我们假设每个建筑物的宽度均为1。

要求输出广告牌的最大面积。

【输入文件】
输入文件 ad.in 中的第一行是一个数n (n<= 400,000)
第二行是n个数,分别表示每个建筑物高度H1,H2…HN,且0<Hi<=1,000,000,000。
【输出文件】
输出文件 ad.out 中一共有一行,表示广告牌的最大面积。
【输入样例】
6
5 8 4 4 8 4
【输出样例】

24

思路:

容易想到,要想使面积最大,肯定要将当前广告覆盖的楼房中,高度最小的那个楼房全部印刷上广告。所以,枚举每个建筑物的高度作为广告矩形的高度,求出在该楼房的左侧,有多少栋连续的楼房的高度大于或等于该楼房的高度,右侧同理。然后得到矩形广告的面积,找出最大面积即可。

枚举法,时间复杂度为O(n^2):

 1 int MaxRectArea(void)
 2 {
 3     int maxArea = 0;
 4     int i, j;
 5     for (i = 0; i < n; i++)
 6     {
 7         int count = 1;
 8         //统计在当前建筑物i的左边,与h[i]相同或更高的建筑物有多少
 9         for (j = i-1; j >= 0 && h[j] >= h[i]; j--)
10         {
11             count++;
12         }
13         //右边同理
14         for (j = i+1; j < n && h[j] >= h[i]; j++)
15         {
16             count++;
17         }
18         int area = count * h[i];
19         if (area > maxArea)
20         {
21             maxArea = area;
22         }
23     }
24     return maxArea;
25 }  

使用单调队列优化,时间复杂度为O(n):

在这里约定,以当前建筑物为矩形高度,向两侧寻找可以印刷的建筑的过程叫做——扩展。

我们来考察从第i个建筑向左扩展的情况,h数组为建筑物高度,下标为1~n:

如果我们知道,向左扩展到极限时的建筑物的下标为j的话,这时,建筑物j是第一个满足h[j]<h[i]的建筑。那么,第i个建筑物向左扩展的建筑物数量为i-j-1。

如果,对h从左向右建立一个单调递增队列mq,h数组的下标为队列元素,在新的建筑物i要入队时,将队尾所有高度大于等于h[i]的元素都出队,新的队尾mq[rear-1](rear指向队尾的下一个位置)刚好是上面所讲的向左扩展的极限下标j,因为入队顺序是从左向右的,而且在极限下标j与当前建筑下标i之间的这些建筑,因为高度大于等于h[i],所有都出队了,mq[rear-1]就刚好是极限下标j。

向右侧扩展是同样的道理,只需要对h从右向左建立一个单调递增队列即可。

为了简化操作,将h[0]和h[n+1]的值都赋为-1。

下面举一个向左侧扩展的例子:

H[]存储建筑物高度,L[]记录向左扩展建筑数量。


下标


0


1


2


3


4


H[]


-1


9


5


5


-1


L[]

单调递增队列,front和rear分别为队首和队尾指针,rear指向队尾的下一个位置,初始时让0号建筑入队


指针


Front


Rear


队列元素


0

1号建筑入队,队尾没有比1号建筑更高的建筑,直接入队,L[1]= 1 - mq[rear-1] - 1,结果如下:


下标


0


1


2


3


4


H[]


-1


9


5


5


-1


L[]


0


指针


Front


Rear


队列元素


0


1

2号建筑入队,队尾元素为1号建筑,高度为9,比2号建筑高,所以出队,新的队尾元素为0号,计算L[2] = 2 –mq[rear-1] – 1,然后2号建筑入队尾,结果如下


下标


0


1


2


3


4


H[]


-1


9


5


5


-1


L[]


0


1


指针


Front


Rear


队列元素


0


2

3号建筑入队,队尾元素为2号建筑,高度为5,与3号建筑一样高,所以出队,新的队尾元素为0号,计算L[3] = 3 –mq[rear-1] – 1,然后3号建筑入队尾,结果如下:


下标


0


1


2


3


4


H[]


-1


9


5


5


-1


L[]


0


1


2


指针


Front


Rear


队列元素


0


3

最终结果:


下标


0


1


2


3


4


H[]


-1


9


5


5


-1


L[]


0


1


2

代码:

 1 #define N 10005
 2 #include<iostream>
 3 using namespace std;
 4 #include<cstdio>
 5 #include<cstring>
 6 int Q[N],h[N],head=0,tail=1,maxar=-1,kzleft[N],kzright[N],n;
 7 void input()
 8 {
 9     scanf("%d",&n);
10     for(int i=1;i<=n;++i)
11       scanf("%d",&h[i]);
12 }
13 void calcleft()
14 {
15     memset(Q,0,sizeof(Q));
16     h[0]=h[n+1]=-1;
17     for(int i=1;i<=n;++i)
18     {
19         while(head<tail&&h[Q[tail-1]]>=h[i])   tail--;
20         kzleft[i]=i-(Q[tail-1]);
21         Q[tail++]=i;
22     }
23 }
24 void calcright()
25 {
26     memset(Q,0,sizeof(Q));
27     head=0;tail=1;
28     Q[0]=n+1;
29     for(int i=n;i>=1;--i)
30     {
31         while(head<tail&&h[Q[tail-1]]>=h[i]) tail--;
32         kzright[i]=Q[tail-1]-i;
33         Q[tail++]=i;
34     }
35 }
36 int find_max_area()
37 {
38     for(int i=1;i<=n;++i)
39     {
40         maxar=max((kzleft[i]+kzright[i]-1)*h[i],maxar);
41     }
42     return maxar;
43 }
44 int main()
45 {
46     input();
47     calcleft();
48     calcright();
49     printf("%d\n",find_max_area());
50     return 0;
51 }
时间: 2024-10-17 07:41:18

单调队列练习之广告印刷的相关文章

【单调队列】广告印刷

至今没有找到出处的题目,但是手里碰巧有一套测试数据,缺测试数据的人可以问我要.经典单调队列,这位的博文说的很清楚,我就不多阐述了. 1 #include<iostream> 2 #include<cstdio> 3 #include<queue> 4 #include<cstring> 5 6 using namespace std; 7 const int MAXN=400000; 8 int llen[MAXN],rlen[MAXN],q[MAXN];

【动态规划】【单调队列】tyvj1305 最大子序和

http://blog.csdn.net/oiljt12138/article/details/51174560 单调队列优化dp #include<cstdio> #include<deque> #include<algorithm> #include<iostream> using namespace std; typedef long long ll; int n,m; ll a[300100],ans; deque<int>q; int

hdu_5884_Sort(二分+单调队列)

题目链接:hdu_5884_Sort 题意: 有n个数,每个数有个值,现在你可以选择每次K个数合并,合并的消耗为这K个数的权值和,问在合并为只有1个数的时候,总消耗不超过T的情况下,最小的K是多少 题解: 首先要选满足条件的最小K,肯定会想到二分. 然后是如何来写这个check函数的问题 我们要贪心做到使消耗最小,首先我们将所有的数排序 然后对于每次的check的mid都取最小的mid个数来合并,然后把新产生的数扔进优先队列,直到最后只剩一个数. 不过这样的做法是n*(logn)2 ,常数写的小

[Vijos 1243]生产产品(单调队列优化Dp)

Description 在经过一段时间的经营后,dd_engi的OI商店不满足于从别的供货商那里购买产品放上货架,而要开始自己生产产品了!产品的生产需要M个步骤,每一个步骤都可以在N台机器中的任何一台完成,但生产的步骤必须严格按顺序执行.由于这N台机器的性能不同,它们完成每一个步骤的所需时间也不同.机器i完成第j个步骤的时间为T[i,j].把半成品从一台机器上搬到另一台机器上也需要一定的时间K.同时,为了保证安全和产品的质量,每台机器最多只能连续完成产品的L个步骤.也就是说,如果有一台机器连续完

单调队列

先放上luogu的题目链接--滑稽窗口 然后我们再来讲单调队列 单调队列是指这样一种队列:在队列中的元素为单调递增状态或单调递减状态. 例如1 2 3 4 5和9 2 1都是单调队列,但1 2 2 3 4和4 3 4 5就不是单调队列. 但普通队列明显是维持不了单调队列的性质的. 为了维持单调队列的单调性质,我们只好想一些方法.方法就是修改队列的性质.单调队列不仅队头可以出队,队尾也可以出队. 比如说有一个单调队列是 1 3 7 8 现在突然要从队尾进来一个6如果单纯的把6插进队尾的话,那这个队

单调队列 BZOJ 2096 [Poi2010]Pilots

2096: [Poi2010]Pilots Time Limit: 30 Sec  Memory Limit: 162 MBSubmit: 819  Solved: 418[Submit][Status][Discuss] Description Tz又耍畸形了!!他要当飞行员,他拿到了一个飞行员测试难度序列,他设定了一个难度差的最大值,在序列中他想找到一个最长的子串,任意两个难度差不会超过他设定的最大值.耍畸形一个人是不行的,于是他找到了你. Input 输入:第一行两个有空格隔开的整数k(0

HDU 3706 Second My Problem First (单调队列)

题意:求给定的一个序列中最长子序列,该子序列的最大值和最小值介于m和k之间. 析:用两个单调队列来维护一个最小值,一个最大值,然后每次更新即可. 代码如下; #pragma comment(linker, "/STACK:1024000000,1024000000") #include <cstdio> #include <string> #include <cstdlib> #include <cmath> #include <i

codevs3327选择数字(单调队列优化)

3327 选择数字 时间限制: 1 s 空间限制: 256000 KB 题目等级 : 钻石 Diamond 题目描述 Description 给定一行n个非负整数a[1]..a[n].现在你可以选择其中若干个数,但不能有超过k个连续的数字被选择.你的任务是使得选出的数字的和最大. 输入描述 Input Description 第一行两个整数n,k 以下n行,每行一个整数表示a[i]. 输出描述 Output Description 输出一个值表示答案. 样例输入 Sample Input 5 2

【NOIP数据结构专项】单调队列单调栈

[洛谷P1901 ]发射站 http://www.luogu.org/problem/show?pid=1901 题目描述 某地有 N 个能量发射站排成一行,每个发射站 i 都有不相同的高度 Hi,并能向两边(当 然两端的只能向一边)同时发射能量值为 Vi 的能量,并且发出的能量只被两边最近的且比 它高的发射站接收. 显然,每个发射站发来的能量有可能被 0 或 1 或 2 个其他发射站所接受,特别是为了安 全,每个发射站接收到的能量总和是我们很关心的问题.由于数据很多,现只需要你帮忙计 算出接收