HDU杭电 1506 (fjutacm 1899) Largest Rectangle in a Histogram 单调栈

Problem Description
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:
HDU杭电 1506 (fjutacm 1899) Largest Rectangle in a Histogram 单调栈_第1张图片
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.题意很简单就是找直方图能嵌套的最大矩形

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

Output
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.

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

SampleOutput
8
4000
3

没错,题意就是单纯的找最大的、连续的矩形区间。

所以开始之前,要先明确我们要维护的是一个递增的单调栈。为什么呢?通过模拟几个简单的案例我们会发现,当一个方块的高度比两边的都高时,它是不可能再往两边扩张的了。

所以,如果我们一直维护一个单调的栈,那么每次我们每次维护这个栈,都只需要做一个循环出栈的操作,并顺手更新答案。最终我们得到的还是一个单调栈,这时候在末尾放一个足够小的数,一次性往回推,一边出栈一边更新答案,最终得到的便是解。

文字我说不清,结合代码和案例更好理解。

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
const int MAX=1e5+5;
typedef long long ll;
typedef unsigned long long ull;
struct node
{
  ll h, w;/// h记录高度,w记录连续区间长度
}q[MAX], a;
int main()
{
  ll n, h, i, j, ans, top, last;
  while (scanf("%I64d", &n)&&n)
  {
    ans = top = 0;/// 初始化,栈一开始是空的,所以栈首位置top在0处
    memset(q, 0, sizeof(q));
    for (i=0; i<n; i++)
    {
      scanf("%I64d", &q[top].h);
      q[top].w = 0;/// 为了不影响维护单调栈、top往前推的操作,刚推入栈的元素
                   ///的w我们先初始化为0

      while (top&&q[top].h<=q[top-1].h)/// top不能为0,前面没元素就没必要维护;
      {                                ///维护单调递增,所以后一个元素小于前一
                                       ///个元素时,我们就需要更新维护了(=可
                                       ///有可无)
        q[top-1].w += q[top].w;/// 因为是单调栈,所以当前元素一定可以扩张到栈
                               ///首。比如案例中,假设我们维护到栈为 1 4 5 了,
                               ///这时候5能向后扩张0个,4能向后扩张5的w个,也
                               ///就是1个,1能想后扩张4的w个,也就是1+1个。所
                               ///以这一轮如果我们直接在5后面放个0一次性推到
                               ///底,我们能得到5*1,4*2和1*3这三个答案
/// w说得更明白一点,就是压入你这个元素时,有多少个元素出栈了。这意味着,这
///个元素“吃”掉了那么多个比它大的元素,纳入了自己的可扩张区间。毕竟只要前面
///的元素大于(等于)当前元素,就说明以当前元素的高度还可以往前继续扩张
        ans = max(ans, q[top-1].h*q[top-1].w);
        q[top-1].h = q[top].h;
        top --;
      }
      q[top].w ++;
      top ++;
    }
    q[top].h = 0;/// 最后一次大清仓,把栈中余下元素能产生的答案全部生成出来,
    q[top].w = 0;///更新解。这里的操作就是直接压入一个恒小于所有栈中元素的新
                 ///元素0,保证它能一路往回推
    while (top&&q[top].h<=q[top-1].h)
    {
      q[top-1].w += q[top].w;
      ans = max(ans, q[top-1].h*q[top-1].w);
      q[top-1].h = q[top].h;
      top --;
    }
    printf("%I64d\n", ans);
  }
  return 0;
}

这题的案例给得还是很良心的,多依照代码,或者这个单调栈的思路,去模拟几遍,马上就能明白要怎么维护、有哪些维护的细节了。

你可能感兴趣的:(单调栈,单调栈)