您的位置:首页 > 其它

POJ2559 Largest Rectangle in a Histogram(比动态规划更快的方法——单调队列)

2017-08-14 11:26 399 查看
  

Largest Rectangle in a Histogram

Time Limit : 2000/1000ms (Java/Other)   Memory Limit : 131072/65536K (Java/Other)
Total Submission(s) : 123   Accepted Submission(s) : 25
[align=left]Problem Description[/align]
A histogram is a polygon composed of a sequence of rectangles aligned at a common base line. The rectangles have equal widths but may have different heights. For example, the figure on the left shows the histogram that consists of
rectangles with the heights 2, 1, 4, 5, 1, 3, 3, measured in units where 1 is the width of the rectangles: 



Usually, histograms are used to represent discrete distributions, e.g., the frequencies of characters in texts. Note that the order of the rectangles, i.e., their heights, is important. Calculate the area of the largest rectangle in a histogram that is aligned
at the common base line, too. The figure on the right shows the largest aligned rectangle for the depicted histogram.
 

[align=left]Input[/align]
The input contains several test cases. Each test case describes a histogram and starts with an integer <i>n</i>, denoting the number of rectangles it is composed of. You may assume that <i>1<=n<=100000</i>. Then follow <i>n</i> integers
<i>h<sub>1</sub>,...,h<sub>n</sub></i>, where <i>0<=h<sub>i</sub><=1000000000</i>. These numbers denote the heights of the rectangles of the histogram in left-to-right order. The width of each rectangle is <i>1</i>. A zero follows the input for the last test
case.
 

[align=left]Output[/align]
For each test case output on a single line the area of the largest rectangle in the specified histogram. Remember that this rectangle must be aligned at the common base line.
 

[align=left]Sample Input[/align]

7 2 1 4 5 1 3 3
4 1000 1000 1000 1000
0

 

[align=left]Sample Output[/align]

8
4000

   这道题以前是放在动态规划里面的,用动态规划虽然时间长但也能AC,但是放在单调队列专题里,动态规划就超时了。首先看看动态规划的做法:

/*

给定从左到右多个矩形,已知这此矩形的宽度都为1,长度不完全相等。

这些矩形相连排成一排,求在这些矩形包括的范围内能得到的面积最大的矩形,打印出该面积。所求矩形可以横跨多个矩形,但不能超出原有矩形所确定的范围。

l[i]表示大于等于h[i]的最左边的位置,r[i]表示大于等于h[i]的最右边的位置,这样可以预处理出l[],r[],然后ans = max(ans, r[i] - l[i] + 1) * h[i])( 1 <= i <= n).*/

TLE:

#include <stdio.h>

#include<math.h>

#include<iostream>

using namespace std;

const int MAXN = 100010;

int l[MAXN], r[MAXN], h[MAXN];

 int main()

 {  int n;

  while(~scanf("%d",&n),n)

   {     int i;

         for(i=1; i<=n; i++)

         { scanf("%d", &h[i]);

           l[i]=r[i]=i;}

        for(i=2; i<=n; ++i)

        while (l[i] > 1 && h[i] <= h[l[i] - 1])

            l[i] = l[i] - 1;

        for(i=n-1; i>=1; --i)

         while (r[i] < n && h[i] <= h[r[i] + 1])

            r[i] = r[i] + 1;

         __int64 ans=0;

        for(i=1; i<=n; ++i)

    ans=max(ans, (__int64)(r[i]-l[i]+1)*h[i]);

            printf("%d\n",&ans);

   }

 }

如果用单调队列的话,复杂度变为O(N),时间快很多:

 AC代码:

【分析】

    这道题目是要求每个建筑物向左向右能扩展到的最大宽度,即左右两边比它高的连续的宽度。显然暴力枚举O(n^2)的复杂度是不可行的(该方法TLE,程序如上)。

    考虑构造一个单调非递减队列,从左至右,依次加入到队列中,肯定会有元素出队列,

    设当前要插入的数为a,要出队列的数为b,必有b>=a,则b向右能到达的最远距离就是b-a。

    注意在求解时,让0先入队列,这样保证每个数据都会出队列。同理,左极限也可求出

*/

#include<stdio.h>

#include<string.h>

#define MAXN 1000000

long long h[MAXN+5];//建筑物的高度

int n;       //建筑物的数目

int mq[MAXN+5];  //单调队列,队内元素为建筑物高度的下标

int lef[MAXN+5]; //left[i]:在第i个建筑物左侧,不比它的高度小的建筑物数量(该建筑物与i相邻且连续)

int righ[MAXN+5]; //right[i]:在第i个建筑物右侧,不比它的高度小的建筑物数量(该建筑物与i相邻且连续)

long long maxArea;

void CalcLeft()

{

 mq[0] = 0;

 int front = 0, rear = 1;

 int i;

 for (i = 1; i <= n; i++)

 {

  while (front < rear && h[i] <= h[mq[rear-1]])

  {

   rear--;

  }

  lef[i] = i - mq[rear-1] - 1;

  mq[rear++] = i;//等价于 mq[rear]=i;i+=1;

 }

}

void CalcRight()

{

 mq[0] = n + 1;

 int front = 0, rear = 1;

 int i;

 for (i = n; i >= 1; i--)

 {

  while (front < rear && h[i] <= h[mq[rear-1]])

  {

   rear--;

  }

  righ[i] = mq[rear-1] - i - 1;

  mq[rear++] = i;

 }

}

void MaxRectArea()

{

 maxArea = -1;

 int i;

 for (i = 1; i <= n; i++)

 {

  long long area = (lef[i] + righ[i] + 1) * h[i];

  if (area > maxArea)

  {

   maxArea = area;

  }

 }

 //return maxArea;

}

int main()

{

 while (~scanf("%d",&n)&&n)

 {

  int i;

  memset(h,0,sizeof(h));

  memset(mq,0,sizeof(mq));

  memset(lef,0,sizeof(lef));

  memset(righ,0,sizeof(righ));

  for (i = 1; i <= n; i++)

  {

   scanf("%d",&h[i]);

  }

  h[0] = h[n+1] = -1;

  CalcLeft();

  CalcRight();

  MaxRectArea();

/*for (i = 1; i <= n; i++)

   {

 cout << lef[i] << ' ';

   }

cout << endl;*/

/*for (i = 1; i <= n; i++)

{

 cout << righ[i] << ' ';

}

cout << endl;

*/

printf("%lld\n",maxArea);

}

 return 0;

}

单调队列虽然原理简单,但不是那么容易理解程序实现的过程,我比较笨,我是根据一个例子一步一步模拟,看出其实现过程的。

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: