單調棧 最大矩形 單調佇列 滑動視窗

2021-10-04 10:42:25 字數 4573 閱讀 9722

給乙個直方圖,求直方圖中的最大矩形的面積。例如,下面這個中直方圖的高度從左到右分別是2, 1, 4, 5, 1, 3, 3, 他們的寬都是1,其中最大的矩形是陰影部分。

輸入包含多組資料。每組資料用乙個整數n來表示直方圖中小矩形的個數,你可以假定1 <= n <= 100000. 然後接下來n個整數h1, …, hn, 滿足 0 <= hi <= 1000000000. 這些數字表示直方圖中從左到右每個小矩形的高度,每個小矩形的寬度為1。 測試資料以0結尾。

對於每組測試資料輸出一行乙個整數表示答案。

721

4513

341000

1000

1000

1000

0

8

4000

初始化

for

(int i=

1;i<=n;i++

)scanf

("%lld"

,&a[i]);

//資料很多 用scanf速度快

memset

(a_l+1,

0,n)

;//將左側第乙個比它小的位置初始化為0

memset

(a_r+

1,n+

1,n)

;//將右側第乙個比它小的位置初始化為n+1

a[0]=

-1, a[n+1]

=-1;

//為了將棧中元素全部彈出

維護乙個單調棧,當 棧非空 並且 當前元素小於棧頂元素時,將棧頂元素依次彈出,並且對其右側第乙個比它小的位置 a_r[st.top()]=i;

for

(int i=

1;i<=n+

1;i++)if

(st.

empty()

||a[st.

top()]

<=a[i]

)//if 其實可以省略,因為while後一定滿足該條件,此處為了方便理解

}

再倒敘來一遍,相當於找左邊能到達的最遠區域,本質是一樣的。

#include

#include

#include

#include

using

namespace std;

long

long a[

100005];

long

long a_r[

100005];

long

long a_l[

100005];

long

long ans;

intmain()

if(st.

empty()

||a[st.

top()]

<=a[i])}

st.pop();

// a[n+1] = -1; //為了將棧中元素全部彈出 最後還在stack裡面

for(

int i=n;i>=

0;i--)if

(st.

empty()

||a[st.

top()]

<=a[i])}

for(

int i=

1;i<=n;i++

) cout<

}}

zjm 有乙個長度為 n 的數列和乙個大小為 k 的視窗, 視窗可以在數列上來回移動. 現在 zjm 想知道在視窗從左往右滑的時候,每次視窗內數的最大值和最小值分別是多少. 例如:

數列是 [1 3 -1 -3 5 3 6 7], 其中 k 等於 3.

輸入有兩行。第一行兩個整數n和k分別表示數列的長度和滑動視窗的大小,1<=k<=n<=1000000。第二行有n個整數表示zjm的數列。

輸出有兩行。第一行輸出滑動視窗在從左到右的每個位置時,滑動視窗中的最小值。第二行是最大值。

831

3-1-

3536

7

-1-

3-3-

3333

3556

7

先順序維護乙個單調遞增的雙向佇列(儲存下標)。

首先將1~k-1號元素push進佇列中並維護單增佇列,如果隊尾元素大於當前元素,則一直pop隊尾元素直到不滿足條件。

for

(int i=

1;i<=k-

1;i++

)//初始化

if(q.

empty()

||a[q.

back()

]<=a[i]

)}

然後從k到n號元素開始,每次先維護佇列的單調性,然後放入i號元素並且維護視窗的大小,具體做法為判斷(i - 隊首元素儲存的下標)是否 > k,(因為隊首元素一定是當前單調佇列最先進來的元素)超過則pop隊首元素 然後將隊首元素存到a_min[i]中。

for

(int i=k;i<=n;i++

)//視窗從k開始向右移動 維護乙個單增佇列

if(q.

empty()

||a[q.

back()

]<=a[i]

)while

(!q.

empty()

&&(i-q.

front()

)>=k)

//再維護視窗的大小 保證此時佇列中都落在視窗中

a_min[i]

=q.front()

;}

同理,維護乙個單減佇列,重複上述操作。(前提把deque清空 clear())

#include

#include

#include

using

namespace std;

int a[

1000005];

int a_min[

1000005];

int a_max[

1000005];

intmain()

if(q.

empty()

||a[q.

back()

]<=a[i])}

for(

int i=k;i<=n;i++

)//視窗從k開始向右移動 維護乙個單增佇列

if(q.

empty()

||a[q.

back()

]<=a[i]

)while

(!q.

empty()

&&(i-q.

front()

)>=k)

//再維護視窗的大小 保證此時佇列中都落在視窗中

a_min[i]

=q.front()

;}q.clear()

;//deque 可以清空佇列

for(

int i=

1;i<=k-

1;i++)if

(q.empty()

||a[q.

back()

]>=a[i])}

for(

int i=k;i<=n;i++

)//視窗從k開始向右移動 維護乙個單增佇列

if(q.

empty()

||a[q.

back()

]>=a[i]

)while

(!q.

empty()

&&(i-q.

front()

)>=k)

//然後維護視窗的大小 保證此時佇列中都落在視窗中

a_max[i]

=q.front()

;}for(

int i=k;i<=n;i++

) cout<

]<<

" ";

cout<

for(

int i=k;i<=n;i++

) cout<

]<<

" ";

cout<

return0;

}

單調佇列的維護過程與單調棧相似

•區別在於

• 單調棧只維護一端(棧頂)

, 而單調佇列可以維護兩端(隊首和隊尾)

• 單調棧通常維護 全域性 的單調性, 而單調佇列通常維護 區域性 的單調性

• 單調棧大小沒有上限, 而單調佇列通常有大小限制

(由於單調佇列 可以隊首出隊 以及 前面的元素一定比後面的元素先入隊 的

性質,使得它可以維護區域性的單調性,當隊首元素不在區間之內則可以出隊)

關於複雜度,遍歷一遍,每個元素只訪問一遍,所以是o

(n)

單調佇列 滑動視窗 模板 單調佇列

有乙個長為 n 的序列 a,以及乙個大小為 k 的視窗。現在這個從左邊開始向右滑動,每次滑動乙個單位,求出每次滑動後視窗中的最大值和最小值。the array is 1,3,1,3,5,3,6,7 and k 3。輸入一共有兩行,第一行有兩個正整數 n,k。第二行 n 個整數,表示序列 a 輸出共兩...

單調佇列 滑動視窗

nkoj 2152 description 給你乙個長度為n n 10 6 的陣列,乙個長為k的滑動的窗體從最左移至最右端,你只能見到視窗的k個數,每次窗體向右移動一位,找出窗體所包含的數字的最大和最小值,如下表所示 k的值為3 視窗位置 最小值 最大值 1 3 1 3 5 3 6 7 1 3 1 ...

滑動視窗 單調佇列

給定乙個大小為n 106的陣列。有乙個大小為k的滑動視窗,它從陣列的最左邊移動到最右邊。您只能在視窗中看到k個數字。每次滑動視窗向右移動乙個位置。以下是乙個例子 該陣列為 1 3 1 3 5 3 6 7 k為3。視窗位置 最小值 最大值 1 3 1 3 5 3 6 7 1 3 1 3 1 3 5 3...