給乙個直方圖,求直方圖中的最大矩形的面積。例如,下面這個中直方圖的高度從左到右分別是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
維護乙個單調棧,當 棧非空 並且 當前元素小於棧頂元素時,將棧頂元素依次彈出,並且對其右側第乙個比它小的位置 a_r[st.top()]=i;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;
//為了將棧中元素全部彈出
再倒敘來一遍,相當於找左邊能到達的最遠區域,本質是一樣的。for
(int i=
1;i<=n+
1;i++)if
(st.
empty()
||a[st.
top()]
<=a[i]
)//if 其實可以省略,因為while後一定滿足該條件,此處為了方便理解
}
zjm 有乙個長度為 n 的數列和乙個大小為 k 的視窗, 視窗可以在數列上來回移動. 現在 zjm 想知道在視窗從左往右滑的時候,每次視窗內數的最大值和最小值分別是多少. 例如:#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<
}}
數列是 [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隊尾元素直到不滿足條件。
然後從k到n號元素開始,每次先維護佇列的單調性,然後放入i號元素並且維護視窗的大小,具體做法為判斷(i - 隊首元素儲存的下標)是否 > k,(因為隊首元素一定是當前單調佇列最先進來的元素)超過則pop隊首元素 然後將隊首元素存到a_min[i]中。for
(int i=
1;i<=k-
1;i++
)//初始化
if(q.
empty()
||a[q.
back()
]<=a[i]
)}
同理,維護乙個單減佇列,重複上述操作。(前提把deque清空 clear())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()
;}
#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...