木板牆問題

一道算法課網教作業題,值得一看。


考古學家在人跡罕至的一塊平地上發現了由一堆木板拼成的牆。令人驚奇的是這些木板的

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

板,所以考古學家決定帶走面積最大的長方形回去研究。
輸入:
首先是整數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;
}



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