index > Data Structures > monotonic queue
引子
POJ 2823 - Sliding Window,對一個長度爲的數組,求每個長度爲的區間的最小值和最大值。
暴力for可以想到一個的做法,當然,這篇文章是講的做法。
隊列
設定一個雙端隊列元素是 ,約束每次取到數組第個元素的時候有這樣幾種情況:
- 當前元素比隊尾元素值小,則不斷彈出隊尾,再把此元素插入。
- 當前元素比隊尾元素值大,則直接把此元素插入。
但再這個問題中還有個限制是區間,那麼每次插入前我們還需要把隊首元素做一次檢查,如果與當前下標距離大於 則不斷彈出隊首。
至此,當前元素處理完畢後,隊列的隊首,就是以當前位置爲右邊界的區間的最小值。
反向思維一下就可以做最大值了。
其他
優化空間
其實很好想到,你有數組了,存下標就夠了。
換個問法
除了遍歷定長子區間
的最值,其實也可以反過來遍歷得到最值區間長
。
比如問一個數組裏,區間最小值不小於x的最大長度是多少。
這時候我們需要的關鍵信息變成了下標,隊首出隊條件變成了與x的比較。
第二個理題,牛客多校的那個題就是類似這樣的問法,不過更復雜,還要考慮差值。
注意
-
STL的
deque
在實際情況下還是會比較慢,建議用數組模擬一個。 -
單調隊列很靈活,常常是作爲題目做法的某一步。
例題
int Kase, n, m;
int a[MAXN];
int ans1[MAXN], ans2[MAXN];
deque<int> rMAX, rMIN;
int main() {
ios_base::sync_with_stdio(0);
cin.tie(0);
cin >> n >> m;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
for (int i = 1; i <= n; i++) {
while (!rMIN.empty() && i - rMIN.front() >= m)
rMIN.pop_front();
while (!rMAX.empty() && i - rMAX.front() >= m)
rMAX.pop_front();
while (!rMIN.empty() && a[rMIN.back()] > a[i])
rMIN.pop_back();
while (!rMAX.empty() && a[rMAX.back()] < a[i])
rMAX.pop_back();
if (rMIN.empty() || a[rMIN.back()] <= a[i])
rMIN.push_back(i);
if (rMAX.empty() || a[rMAX.back()] >= a[i])
rMAX.push_back(i);
if (i >= m) {
ans1[i] = a[rMIN.front()];
ans2[i] = a[rMAX.front()];
}
}
for (int j = m; j <= n; ++j) {
cout << ans1[j] << " \n"[j == n];
}
for (int j = m; j <= n; ++j) {
cout << ans2[j] << " \n"[j == n];
}
return 0;
}
牛客883F - Planting Trees 單調隊列雙指針