1. 程式人生 > >poj 2559(單調棧)

poj 2559(單調棧)

ACM題集:https://blog.csdn.net/weixin_39778570/article/details/83187443
題目:http://poj.org/problem?id=2559
題意:求最大子矩形的面積.

解法:

把高度看成一個序列,當高度遞增的時候,答案在這個遞增序列往回尋找。例如,
1,2,3,4,5,6;更新答案的時候有這麼幾個選擇(6x1),(5x2),(4x3),(3x4),(2x5),(1x6).(高x寬)
當高度出現下降的時候,這時左端比它高的矩形有一部分是無用的。
例如,1,2,3,4,5,6,4;可以看出1,2,3,4,4,4;因為我們採取往左更新的方式,5,6這兩個大於4的部分是無法被使用上的(往右更新也一樣)。所以我們可以把5,6,4這三個矩形合併成一個高度為4,寬度為3的矩形,使得整個序列單調遞增.
以下是單調棧演算法

/*單調棧*/
#include<cstdio>
#include<algorithm>
#define ll long long
#define fo(i,j,n) for(register int i=j; i<=n; ++i)
using namespace std;

int n;
ll h[100005],w[100005],s[100005]; // 單調遞增棧 
void solve(){
	ll ans = 0;
	h[n+1] = 0; // 最右端設定為0,以便清空棧,遍歷完整個棧 
	int pos = 0;
	fo(i,1,n+1){
		if(s[pos]<=h[i])
{ // 單調不下降,入棧 s[++pos]=h[i]; w[pos]=1; }else{ int width = 0; while(s[pos]>h[i]){ // 往回走,每往回走一步都是一個矩形 width += w[pos]; ans = max(ans, s[pos]*width); // 取棧頂,往回更新 pos--; // 刪除棧頂元素 } s[++pos] = h[i]; // 合併成新的矩形入棧 w[pos] = width+1; } } printf("%lld\n",ans);
} int main(){ while(scanf("%d",&n)&&n){ fo(i,1,n)scanf("%lld",&h[i]); solve(); } return 0; }