木板墙问题

一道算法课网教作业题,值得一看。


考古学家在人迹罕至的一块平地上发现了由一堆木板拼成的墙。令人惊奇的是这些木板的

宽度都相同!地下的部分都已腐烂,而地上的部分也有高有低,甚至有的地方根本没有木

板,所以考古学家决定带走面积最大的长方形回去研究。
输入:
首先是整数n(1<=n<=100000),表示木板的块数。接下来是n个整数h1,...,hn, 其中

0<=hi<=1000000000,它们按照从左到右的顺序表示木板的高度。每块木板的宽度都是1。

最后一个0表示程序的结束。
输出:
其中最大长方形的面积。

测试用例:

测试输入

  1. 3 2000 2000 2000↵
  2. 7 1 2 5 6 1 3 3↵
  3. 0↵

期待的输出

  1. 6000↵
  2. 10↵


方法一:暴力,复杂度O(n^2),直接对于每个数找能以他为最小板处理的个数。

方法二:暴力优化,复杂度O(nlogn)<O()<=O(n^2),在暴力的基础上,l[i],r[i]分别记录第i个板往前遇到的比他小的第一个板的位置,和向后搜索遇到的比他小的第一个板的位置,如果对于当前板i往前找l[i],搜到第j个板时,若j板长与i,且l[j]为0(即向左不存在比他小的板),那么l[i]也就为0不用向前搜索了。向右同理。

方法三:单调栈。复杂度O(n)。

建立木板数据结构a包含pre(用于记录在他之前连续的大于他且不在单调栈里面的木板个数,作用下面讲),i(木板的位置),v(木板的长度)。

步骤:

①i建立单调递增的栈s。

②从0至n-1枚举。

③若当前a[i].v>=s.top().v,直接进栈,否则s.pop(),直到满足条件。

④每次s.pop()的时候,需要计算以出栈木(temp)为最低高度所能达到的面积,并对a[i].pre进行更新,即ans=max(ans,temp.v*(i-temp.i+temp.pre));

⑤结束后,若s不为空,需要依次出栈计算面积。


下面解释:最重要的式子    S=temp.v*(i-temp.i+temp.pre);

我们已知 temp.v是大于a[i].v的,所以temp木板向右最多到达i-1。而向左,为记录的temp.pre。


接下来是关于temp.pre的记录,每次s.pop(),则为大于a[i].v的数出栈,a[i].pre+1,除此之外,还要加上temp本身的temp.pre。


注意:数据很大,需要long long。


代码:

#include <iostream>
#include <stdio.h>
#include <stack>
#include <algorithm>
using namespace std;

int n;
long long ans;

struct p{
	int pre;
	int i;
	long long v;
}a[100010];
stack <struct p> s;

int main()
{
	struct p temp;
	while(~scanf("%d",&n)&&n)
	{
		while(!s.empty())
			s.pop();
		ans=-1;
		for(int i=0;i<n;i++)
		{
			scanf("%lld",&a[i].v);
			a[i].i=i;
			a[i].pre=0;
		}
		for(int i=0;i<n;i++)
		{
			while(!s.empty()&&a[i].v<s.top().v)
			{
				temp=s.top();
				s.pop();
				a[i].pre+=temp.pre+1;
				ans=max(ans,temp.v*(long long)(i-temp.i+temp.pre));
			}
			s.push(a[i]);
		}
		while(!s.empty())
		{
			temp=s.top();
			s.pop();
			ans=max(ans,temp.v*(long long)(n-temp.i+temp.pre));
		}
		printf("%lld\n",ans);
	}
	return 0;
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章