POJ 2559 最大矩形面积 poj 3494

来源:互联网 发布:海思四核3798m阿里云 编辑:程序博客网 时间:2024/05/19 21:02

给定从左到右多个矩形,已知这此矩形的宽度都为1,长度不完全相等。这些矩形相连排成一排,求在这些矩形包括的范围内能得到的面积最大的矩形,打印出该面积。所求矩形可以横跨多个矩形,但不能超出原有矩形所确定的范围。

建立一个单调递增栈,所有元素各进栈和出栈一次即可。每个元素出栈的时候更新最大的矩形面积。

设栈内的元素为一个二元组(x, y),x表示矩形的高度,y表示矩形的宽度。

若原始矩形高度分别为2,1,4,5,1,3,3

高度为2的元素进栈,当前栈为(2,1)

高度为1的元素准备进栈,但必须从栈顶开始删除高度大于或等于1的矩形,因为2已经不可能延续到当前矩形。删除(2,1)这个元素之后,更新最大矩形面积为2*1=2,然后把它的宽度1累加到当前高度为1的准备进栈的矩形,然后进栈,当前栈为(1,2)

高度为4的元素进栈,当前栈为(1,2) (4,1)

高度为5的元素进栈,当前栈为(1,2) (4,1) (5,1)

高度为1的元素准备进栈,删除(5,1)这个元素,更新最大矩形面积为5*1=5,把1累加到下一个元素,得到(4,2),删除(4,2),更新最大矩形面积为4*2=8,把2累加到下一个元素,得到(1,4),1*4=4<8,不必更新,删除(1,4),把4累加到当前准备进栈的元素然后进栈,当前栈为(1,5)

高度为3的元素进栈,当前栈为(1,5) (3,1)

高度为3的元素准备进栈,删除(3,1),不必更新,把1累加到当前准备进栈的元素然后进栈,当前栈为(1,5) (3,2)

把余下的元素逐个出栈,(3,2)出栈,不必更新,把2累加到下一个元素,当前栈为(1,7),(1,7)出栈,不必更新。栈空,结束。

最后的答案就是8。

 

 

 

 

分析:

如果采用枚举的方式,如果当前我们枚举项是 i = 0, 即 height = 2,

我们用另外两个变量 j 和k 向左和向右两个方向搜素,找到第一个 小于 height的下标,这样我们就找到了用 i 项作为高度长方形了。

我们假设 -1位置,和最右高度都是无穷小。

例如:

i = 0, j = -1, k = 1, 最后的面积是 (k - j - 1) * height = 2

i = 1, j = -1, k = 7, 最后面积是( k - j - 1) * height = 7;

...

i = 3, j = 2, k = 5 面积是 ( k - j - 1) * height = 8

枚举出所有的长方形的同时,然后得到最后的面积。

不过这样的程序的时间复杂度是 O(n^2)

我们如何能仅仅做一次,就求出这个面积呢?

观察:

当我们扫扫描到第一个高度 H1 = 2的时候,我可以标记它的起始位置1, 因为我们还不知道它将向右扩展到什么地方,所以继续扫面。

当遇到第二项 H2 = 1, 因为这项比之前的小,我们知道,用H1做高度的长方形结束了,算出它的面积。

同时这个时候,我们多了一个高度H2,用它做长方形高度的长方形起始位置应该是在哪里呢? 因为H1的高度比H2要高,所以这个起始位置自然是H1所在的位置。


为了模拟上面的过程,我们引入单调栈~

我们先定义我们我们要保存的每一项数据

struct Node

{

      int height;

      int startPosition;

};

用来描述某一个高度,和这个高度的起始位置。

然后我们按照高度来组织成单调栈。我们来看一下它是如何工作的。

为了不用考虑堆栈为空的情况,我们用插入栈底 一个高度(0, 0)的项。

数据:

 2 1 4 5 1 3 3

这样初始化

(0 , 0)

I = 1

当扫描到(2, 1)时候,因为高度2 大于栈顶,插入

(0, 0),  (2, 1)

I = 2:

当扫描到1的时候,因为1小于栈顶高度2, 我们认为栈顶的那个高度应不能再向右扩展了,所以我们将它弹出

这个时候扫描到 i = 2;

高度是 (i - 1(H1.startIndex)) * H1.height = 2;

我们得到一个面积是2的长方形。

同时我们发现高度是1的当前高度,可以扩展到 H1所在的下标,所以我们插入( 1, 1) 堆栈变成

(0, 0), (1, 1) 因为(2, 1)已经不能向右伸展了,已经被弹出了


i = 3

(0, 0), (1, 1), ( 4 3)

i = 4

(0, 0), (1, 1), (4, 3), (5, 4)

i = 5

这个时候当前高度小于栈顶高度,我们认为栈顶已经不能向右扩展,所以弹出,并且获得面积 ( i  - H5.startindex) * H5.height = (5 - 4 ) * 5 = 5

弹出这个元素后,其实(4, 3)的高度也要比 1 大,所以把这个也弹出来,同样方式获得面积 8.

最后我们的堆栈是

(0, 0) , (1, 1)

i  = 6

(0, 0), (1, 1), ( 3, 6)

i = 7

(0, 0), (1, 1), (3, 6)

i = 8

最后一步是有点特殊的,因为我们必须要把所有的元素都弹出来,因为栈里面的高度,都坚持到了最后,我们要把这些高度组成的长方形拿出来检测。

我们可以假设扫面到8的时候,高度是0,(最小值)

弹出(3,6)获得面积 (8 - 6 ) * 3 = 6

弹出(1, 1)获得面积(8 - 1) * 1 = 7


最后的面积是8.

 

 

 

 

#include <iostream>#include <cstdio>using namespace std;const int N = 100005;struct Elem{int height;int count;};Elem stack[N];int top;int main(){int height, n;long long ans, tot, tmp;while (scanf("%d", &n) != EOF && n){top = 0;ans = 0;for (int i = 0; i < n; ++i){scanf("%d", &height);tmp = 0;while (top > 0 && stack[top - 1].height >= height){tot = stack[top - 1].height * (stack[top - 1].count + tmp);if (tot > ans) ans = tot;tmp += stack[top - 1].count;--top;}stack[top].height = height;stack[top].count = 1 + tmp;++top;}tmp = 0;while (top > 0){tot = stack[top - 1].height * (stack[top - 1].count + tmp);if (tot > ans) ans = tot;tmp += stack[top - 1].count;--top;}printf("%lld\n", ans);}return 0;}

 

 

 

 

 

 

 

 

 

 

 

 

 


 

题目简述

题目的描述很简单,在一个M * N的矩阵中,所有的元素只有0和1, 找出只包含1的最大矩形。

例如:图中是一个4 × 6的矩形,画出红色的是我们要找到的区域。



最开始见过这个题目是在看一个人写的面经里面,当时完全没有感觉,不知道怎么做。后来知道了一个东西叫单调栈然后做了一些题,居然发现POJ上的这个题目,和那个面试题一模一样。

所以就研究明白,分享一下。


题目分析:

这题目如果用暴力做的话,方法是很显然的,对图中的任意点,(i, j), 向它的右下侧找到一个右下端点( r, u), 然后检测方块 (i, j) -> (r, u) 是不是全1. 如果是全1, 我们就找到一个合适的矩形1. 在枚举的同时更新找到最大矩形。


如果这样的话,枚举复杂度是O(M * N), 找下端点 复杂度是 O (M * N), 然后检测的复杂度也是 O(M*N) 最后算法的复杂度是 O(M^3 * N^3)

显然这个复杂度过了点。


仔细观察发现(这个思路也是别人提醒的,感觉自己现在依然不能很准确的建模):

因为我们要找的是矩形,所以它一定是以 某个行元素开始的,如果枚举行的时候,我们会发现:

对于第一行:



对于第二行:



第三行:



第四行:



这样的话,其实我们要找到的某个矩形就转换成 一某一个行开始的 histogram的最大矩形问题了。

那么我们原始矩形可以变成如下的形式的数据:


第一行表示,我们以第一行作为底边,所形成的 histogram的高度,其他行也类似。

所以问题变成了 枚举每一行,然后求出每一行对应的histogram的最大矩形。

关于histogram求最大矩形

    可以看看     http://blog.csdn.net/hopeztm/article/details/7868581

因为到了这里以后,基本的原理和histogram是一样的,所以就不再重复了。

 

0 0
原创粉丝点击