單調佇列是單調性的一種運用,透過觀察在過濾掉一些不可能、或不需考慮的元素後,留下來的正好會是一個具單調性的序列時,利用其單調性來優化計算量的一種方法。
以下透過例題來理解單調佇列通常用在什麼地方,以及如何使用。
這題的暴力解非常直覺:對每個元素往左找到第一個比自己小的元素,停下來並輸出它。
不過,考慮邪惡一點的測資:一個遞減的序列。這會導致每個元素的答案都是
順序是重要的,所以也不能排序再來二分搜尋。
在這題,因為答案本身就有
先假設每個元素必定相異(相同的情況可以等分析完再來考慮),任取兩個元素
只要考慮好
對於候補超過
它們之間的關係只有兩種:
考慮第
考慮第
因此在
由此結論,對任意數量的候補,只要存在任意數對
這裡回應了前述的「只需考慮
刪到最後,必不存在任何數對
而
考慮新進的
右邊大於等於
這個分界線可利用單調性進行二分搜尋,不過因為之後會刪除掉,所以直接窮舉也可以。
每個元素只會被加進候補
實作上由於只會從最右邊開始移除或插入,因此使用 stack 即可維護。
// 因為常用到 stack 最末尾元素,定義為最後一個元素所在位置比較方便
int tail = 0;
// 用來擋 stack 為空時的特例
val[0] = -1e9;
// 需要記錄位置用來輸出
idx[0] = 0;
for (int i=1; i<=n; i++)
{
// 排除所有比 ary[i] 大的元素
for (; tail>0&&ary[i]>=val[tail]; --tail);
// 排除完後剩下都比 ary[i] 小,此時 tail 是最右邊最靠近的
ans[i] = idx[tail];
// 把 ary[i] 作為新候補塞進 stack
++tail;
val[tail] = ary[i];
idx[tail] = i;
}
直觀的暴力解同樣非常單純,只要窮舉所有的區間起點,再窮舉
由於存在負數,也不能保證區間長度與區間和的關係,固定起點時的區間和也不存在單調性,反例從 sample 就能找到。
考慮固定區間終點
由於
問題被轉成求區間最小值,即經典 RMQ 問題,但這問題太難,所以先考慮是否有機會刪減一些候補。
考慮兩個元素
考慮第
考慮第
相等的情況和第
因此,把不可能被選中的候補刪去的話,剩下的候補會是嚴格遞增序列。
和前一題不同的是,所求為最小值,而遞增序列的最小值位於最左邊。另一處不同點是左側候補有可能因為距離超過
求遞增序列中最小值
實作上兩頭都會變動,因此兩頭都需要記錄。
// head, tail 頭尾包含
int head = 0, tail = 0, ans = 0;
val[0] = 0;
idx[0] = 0;
for (int i=1; i<=n; i++)
{
// 排除距離超過 k 的部份
if (idx[head] <= i-k)
{
++head;
}
// 遞增序列最小值即為序列最左側元素
ans = max(ans, sum[i]-val[head]);
// 排除所有比 sum[i] 大的元素 (sum 為前綴和)
for (; tail>=head&&sum[i]<=val[tail]; --tail);
// 把 sum[i] 作為新候補塞回去
++tail;
val[tail] = sum[i];
idx[tail] = i;
}
很直觀地窮舉所有可能的區間,可以得到一個
固定終點的話,儘管序列全是正整數,區間長度越長總和越大,但區間最小值也可能更小,因此不具備單調性。
不過,考慮區間最小值不變的前提,還是具備單調性的,在此前提下區間越長越好。
因此,可以得到一個結論:最小值相同時,區間長度越長,心情就越好。
而最小值改變的前提是區間往左右擴張時,加進比目前最小值更小的值。所以對每個元素,若能找到兩邊比自己小的值中,最靠近自己的,這兩個值就各會是維持最小值不變時,左右的擴張極限。
如上圖,以塗藍色的部份為最小值時,往兩邊擴張到撞到小於自己的值為止,如圖中的藍線所示,這段區間就是這個最小值的最佳解。窮舉每個元素作為最小值,取算出來的結果最大的,即為所求。
此時問題轉為如何快速對每個最小值
有注意到什麼嗎?沒錯,轉換後的問題和本篇講解的第一題(CSES 1645)所求一模一樣。右邊界可以用相同方式求得,或者將序列反轉後當作左邊界來求也可以。
於是得到在
同樣考慮每個元素作為最小值的情形,對任意元素
在第
但在第
因此只留下有意義的候補時,必為遞增序列。在加進新元素
也就是說,每個元素在被加進序列時會找到左邊界,被移出序列時會決定右邊界,故移出時可順便結算。
為了便於計算,可在最左邊加進高度
為了不讓最右邊移除掉最左邊,所以最左用
這個做法只要
這題有蠻多做法能 AC 的,畢竟
這題看起來沒給矩陣大小的範圍,完全是中譯的問題,切到原文就會看到很明確的規定了 不過沒講最多幾組 test cases
一個單純的想法是窮舉所有可能的矩陣上下界,對每個上下界窮舉每個直行是否在範圍內全都是
若嘗試固定矩形下緣,找到貼著這個下緣的所有矩形最大面積,則只要窮舉
看起來就像這樣,是不是好像有點印象?
觀察會發現若決定將矩形左、右界定在
是不是和上一題的「區間內最小值
於是發現可以套用幾乎相同的做法:在放進序列時找左界,移除時決定右界並結算,可在
競程:二章後半
, 競程