一道算法課網教作業題,值得一看。
考古學家在人跡罕至的一塊平地上發現了由一堆木板拼成的牆。令人驚奇的是這些木板的
寬度都相同!地下的部分都已腐爛,而地上的部分也有高有低,甚至有的地方根本沒有木
板,所以考古學家決定帶走面積最大的長方形回去研究。
輸入:
首先是整數n(1<=n<=100000),表示木板的塊數。接下來是n個整數h1,...,hn, 其中
0<=hi<=1000000000,它們按照從左到右的順序表示木板的高度。每塊木板的寬度都是1。
最後一個0表示程序的結束。
輸出:
其中最大長方形的面積。
測試用例:
測試輸入
- 3 2000 2000 2000↵
- 7 1 2 5 6 1 3 3↵
- 0↵
期待的輸出
- 6000↵
- 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;
}