POJ2559/SP HISTOGRA - Largest Rectangle in a Histogram

大致题意:
有一个直方图,问你能在里面框出多大的矩形。
暴力翻译
好的那么我们来做题。
首先我们可以枚举矩形的左右两边,然后暴力枚举两边之间的最矮矩形高度,然后强行乘起来
O ( n 3 ) O(n^3) O(n3)……10W的数据肯定过不了的……

所以我们珂以加一个优化:用线段树维护区间内最矮的矩形高度
O ( n 2 log ⁡ n ) O(n^2\log n) O(n2logn)好多了是不是

好吧其实我们可以在枚举右边缘时一个一个记录,可以加速到 O ( n 2 ) O(n^2) O(n2)
但是它还是会超时……

有一种一般不达到上界的 O ( n 2 ) O(n^2) O(n2)
枚举左边界,然后使劲往右边扩展。
但是还是不够快!

卡常大法好!

所以我们就可以搬出一个救星:单调栈
这个东西可以把我们的算法直接加速到 O ( n ) O(n) O(n)(好多了)
(为了区分,我将每一个直方图中的长条称为长方形,将枚举得到的更大的图形称为矩形)

首先我们看这一幅图:

我们换一种搜法,枚举右边缘,再往左边扩展。
枚举右边缘,枚举到第4个长方形左边时会发现:

第四个长方形把前面的长方形全部挡住了!框起来的部分就没有用了!
那枚举第五个之后的右边缘时就完全不需要考虑前面的高度有多少啊!

所以我们可以把它合并成一个长方形,直接用最后的长方形高度当作它的高度。

另外,有一些枚举是没有意义的,比如:

这种情况下可以向右扩张得到一个更大的矩形。

结合以上两种情况,我们得到一个优化后的方法:
首先把第一个长方形压入栈。
从左往右搜索,遇到一个新的长方形就检测一下:它的高度是否高于栈顶的长方形?
如果高于,那就把它也压进栈。
如果它没有上一个长方形高,我们就把上一个长方形弹出来,把它的宽度加入新的长方形,再检测以它的高为高的矩形的面积。
说不清楚,上图



这3幅图描述了一次更新的过程。
蓝色的方框记录矩形可能扩展的宽度,红色方框记录前面的长方形可能得到的最大矩形。
最终,把最后一幅图中的蓝框框压进栈里。

这样的过程其实是维护了一个单调上升的序列(因为如果有下降就会被删掉),又因为它是一个栈,我们就叫它单调栈。

因为每一个长方形最多入栈一次,每一个原长方形也只能产生一个新长方形,所以时间复杂度是 O ( n ) O(n) O(n)

呼~~~终于讲完了!

接下来是愉快的贴代码时间:
blog完成于2019.5.6,代码贴于2019.6.27

#include<cstdio>
#include<cstring>
using namespace std;
long long max(long long a,long long b)
{return a>b?a:b;}
struct stack
{
	int st[110000],tp;
	void push(int x){st[++tp]=x;}
	int top(){return st[tp];}
	void pop(){--tp;}
	int ppt(){return st[tp--];}//弹出栈顶并获取其值
}st,w;//此处是栈的数据结构,可以#include<stack>后使用STL中的stack<int>代替(ppt()需另写)
//st储存高度,w储存宽度
void reset()
{st.tp=w.tp=0;}
int n,a[110000];
void Main()
{
	long long ans=0;
	for(int i=1;i<=n;i++)
		scanf("%d",a+i);
	a[n+1]=0;//需定义右边界(诱发最后一次清算)
	for(int i=1;i<=n+1;i++)
	{
		int cnt=0;
		while(a[i]<st.top()&&st.tp)//形成单调性
			ans=max(ans,1ll*st.ppt()*(cnt+=w.ppt()));//找红色矩形
		st.push(a[i]),w.push(cnt+1);//添加新矩形
	}
	printf("%lld\n",ans);
}
int main()
{
	while(~scanf("%d",&n)&&n)
		reset(),Main();
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值