單調佇列與單調棧 初步
雖然是個不算複雜的資料結構,但是總感覺自己好像不太會,所以做一些題目以期能更好地掌握該知識點。
p5788 【模板】單調棧
對於每個元素而言,要找在其之後第乙個大於它的元素,所以我們可以維護乙個單調減棧;當新進入的元素進入後將之前某些小的元素擠出棧後,由於單調棧的性質可知這個元素就是大於之前那些等待配對的出棧元素的第乙個元素,所以就可以記錄出棧元素的答案。
p1886 滑動視窗 /【模板】單調佇列
以區間最小元素的求解為例。
維護乙個單調增的佇列,當進入元素小於隊尾元素,將其下沉並擠出那些大於它的元素;與此同時我們還需要保證最小的元素在k區間內,所以在隊頭要將那些序號已經不在當前k區間的元素退隊。
p1823 coi2007 patrik **會的等待
如果當前某個人之後進入了某個更高的人,那麼此人就會被遮擋而無法有可能與之後的人配對,所以根據這個特性就可以維護乙個單調減棧。當進入乙個新人後,單調棧中那些高度不高於他的人均可以與其配對(並出棧),除此以外如果還有乙個比他高的人也可以進行一次配對,不過再往高處就不能看見了。
雖然理清了解答辦法但是我寫wa了…而且不知道怎麼wa了…
學習一下簡潔漂亮的標答:
using par = pair<
int,
int>
;stack sta;
intmain()
;while
(!sta.
empty()
&& sta.
top(
).first <= x)if(
!sta.
empty()
) ans++
; sta.
push
(temp);}
printf
("%lld\n"
, ans)
;return0;
}
poj2559 largest rectangle in a histogram
最大矩形覆蓋。
在這個序列中,如果出現了某個逆序對,那麼高矩形的高度就無法再往後傳遞了;據此我們就可以得知應該使用單調增的單調棧模型。
一旦某個較矮的矩形進入,那麼就應該彈出棧頂元素並求該高度可能覆蓋的的面積最大值。因為單調棧的單調性,所以某個彈出元素的序號到當前元素序號之間的所有矩形高度都是大於等於彈出高度的,所以可以得到右矩形的最遠延伸距離。而左邊最遠的延伸距離呢?可以考慮一組資料(5; 2 1 2 1 2),如果當之後出現的某些元素擠出之前的那些較高元素後,其實左邊的高矩形不是被真正地移除,而是被截斷
,所以可以為每個高度賦乙個權值(可以理解為長度),這個較矮高度的矩形進入單調棧後每擠掉乙個原有矩形就不斷累加它的長度。
這樣子在更新某高度最大面積覆蓋的時候就可以利用權值(長度)、出棧元素序號和高度以及進棧元素的序號解得。
struct node
;stack sta;
intmain()
;while
(!sta.
empty()
&& x <= sta.
top(
).x)
sta.
push
(temp);}
printf
("%lld\n"
, ans);}
return0;
}
poj2796 feel good
找一段區間使得其區間元素和乘上區間最小值的結果最大。
如果之後某個元素的值較小,那麼之前那些更大的最小值顯然就無法貢獻到之後的區間了,因而我們選擇單調減的單調棧。
與上題類似地考慮那些出棧元素對答案的更新問題。記錄字首和,用出棧元素前乙個元素的位置和當前進棧元素的位置即可獲得這個出棧元素所能貢獻到的最長區間,乘上其本身的值即可獲得這一段區間的幸福值。不斷更新答案即可。
const
int maxn =
1e5+10;
struct node
;stack sta;
ll s[maxn]
;int
main()
;while
(!sta.
empty()
&& sta.
top(
).x >= x)
} sta.
push
(temp);}
printf
("%lld\n%lld %lld\n"
, ans, ansl+
1, ansy +1)
;return0;
}
(ansy打錯,實為ansl)
有乙個小wa點,ansl和ansr初始化不應該選為-1,因為如果輸入序列為全0時,max不會執行更新操作,所以左右區間也就不會被更新;置為0即可解決問題。
poj3250 bad hair day
找每個元素之後的那些比他小的元素,一旦遇到不小於它的元素即停止尋找;求每個元素之和。
最開始讀錯題意了,以為只有遇到那些嚴格大於它的元素才會停止搜尋。
用乙個單調減的單調棧即可:當之後某個元素較高時會擋住之前較矮的元素,所以就應該擠掉那些小元素。
const
int maxn =
8e4+10;
struct node
;stack sta;
int ans[maxn]
;int
main()
;while
(!sta.
empty()
&& x >= sta.
top(
).h)
sta.
push
(temp);}
while
(!sta.
empty()
) ll sum =0;
for(
int i =
0; i < n; i++
) sum +
= ans[i]
;printf
("%lld\n"
, sum)
;return0;
}
今天沒做幾個題,日後有機會做一下更加困難的單調棧單調佇列相關的題目。
總而言之,單調棧的核心思想在於貪心
, 只記錄那些以後還可能有貢獻的元素,而將對之後情況絕對無貢獻的元素去除掉。
單調佇列與單調棧
單調棧 單調棧,顧名思義,就是維持單調性 遞增或者遞減 的棧結構,如果新入棧的元素破壞了單調性,就彈出原先棧內元素,直到能夠滿足單調性 用途 它可以很方便地求出某個數的左邊或者右邊第乙個比它大或者小的元素,而且總時間複雜度o n 並且單調棧本身並不難實現 維護 每次入棧前先檢驗入棧後是否會破壞棧的單...
單調佇列與單調棧
線段樹等等容易tle,我們需要乙個o n 的演算法來解決這個問題。思路 可以看出,這個視窗是可以用乙個雙向佇列來模擬的,每當後方加入乙個數,都要從佇列尾部開始淘汰掉所有的小於它的數,保證佇列中每乙個數的後面,在視窗範圍內沒有大於等於它的數。當隊首元素不在視窗範圍時,隊首元素出隊。這樣操作後,隊內的元...
單調佇列與單調棧
線段樹等等容易tle,我們需要乙個o n 的演算法來解決這個問題。思路 可以看出,這個視窗是可以用乙個雙向佇列來模擬的,每當後方加入乙個數,都要從佇列尾部開始淘汰掉所有的小於它的數,保證佇列中每乙個數的後面,在視窗範圍內沒有大於等於它的數。當隊首元素不在視窗範圍時,隊首元素出隊。這樣操作後,隊內的元...