單調棧小結

知識的遺忘率真是太高了。。所以就隨便寫一下
下面我們僅舉單調上升的單調棧的例子,讓讀者(主要是我)對單調棧的實現有一定的概念。
單調棧是在序列上維護的,我們掃描這個序列
實現的方法是這樣的:

while(sn&&x<=sta[sn])
			--sn;
		sta[++sn]=x;
		a[sn]=i;

其中sta表示棧中元素的大小,a表示棧中元素的位置,x表示當前元素,pos表示當前元素的位置。
就是當前的元素比棧尾小的時候總是彈出棧尾,在棧尾小於x的時候將x插在棧尾
單調棧的性質:
性質1:容易驗證,對單調棧中的下標爲i的元素,其必然爲a[i]至pos-1中最小的,否則i必然已經被彈出。
性質2:同樣的,sta[i]必然也是[a[i-1]+1,a[i]]中最小的,否則若該區間存在比sta[i]中更小的元素,其必然比sta[i-1]大,否則與性質1矛盾。則該元素大小在(sta[i-1],sta[i])之間,則其必然已經插入了棧,並使得i的下標後移一位。
容易驗證,單調棧實際上表明瞭從某個點開始向左向右延伸,使其拓展到左邊界-1即a[i-1]的元素sta[i-1]小於他,右邊界+1即pos的元素x小於他。這說明如果一個題目要在序列上面進行min操作,單調棧可以直觀的表示出一個元素可以“控制”的區間的範圍。
寫代碼時可能會錯誤的地方:
在插入單調棧時元素的左邊界已經顯然了,但是右邊界僅在該元素被彈出時確定。
對於序列已經處理完後,最後留在棧中未被彈出的元素一般需要退完棧。其控制的右邊界顯然爲a[i]至n.

至此,我們得到了一個可以便捷的算出各個元素的控制區間的O(n)的算法

舉一個栗子
有n個柱子,找出一個區間l,r使得min(h[i])*(r-l+1),l<=i<=r,最大化。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<string>
#include<cstring>
using namespace std;
const int N=1000;
const int dx[]={0,1,0,-1};
const int dy[]={1,0,-1,0};
int T,n,m,x,ans,res,last,u,v;
int a[N],sta[N],sn;
int pd;
int main()
{
	cin>>n;
	for(int i=1;i<=n;++i)
	{
		cin>>x;
		ans=max(ans,x);
		while(sn&&x<=sta[sn])
		{
			ans=max(ans,(i-1-a[sn-1])*sta[sn]);
			--sn;
		}
		sta[++sn]=x;
		a[sn]=i;
	}
	while(sn)
	{
		ans=max(ans,(n-a[sn-1])*sta[sn]);
		--sn;
	}
	printf("%d",ans);
}


說實話這樣子我有點害怕到時候代碼查重判我抄網上的代碼。(滑稽)

發佈了169 篇原創文章 · 獲贊 7 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章