zoukankan      html  css  js  c++  java
  • 单调队列练习之广告印刷

    单调队列之广告印刷

    【问题描述】
    最近,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
    ",find_max_area());
    50     return 0;
    51 }
  • 相关阅读:
    Solr查询参数sort(排序)
    使用SolrNet访问Solr-5.5.0
    java impl
    Solr Facet 搜索时,facet.missing = true 的真正含义
    为solr增加用户验证
    每日晨读_20140705
    说说常用的服务器操作
    如何添加自定义脚本到开机自启动
    记录一个mysql连接慢的问题
    javascript时间戳和日期字符串相互转换
  • 原文地址:https://www.cnblogs.com/c1299401227/p/5774479.html
Copyright © 2011-2022 走看看