# 單調隊列 ## 概念 我們直接帶題目 [題目](https://zerojudge.tw/ShowProblem?problemid=a146) 我們像題目一樣想像一個框框 像右一次就pop一個push一個 單調對列也就是用deque儲存這個框框內有可能有利用價值的數 當deque裡的值超出框框就pop掉 有更有利用價值的出現就把前面比較沒有利用價值的的pop掉 這題的利用價值就是數的大或小 假設有兩數a,b,且a比b更有利用價值 如果a比b更右邊出現,那麼當a出現在框框後b就會變得無用 如果b比a更右邊出現,那麼兩數就都還有利用價值 所以越有價值的會在越靠近deque的front的地方(有價值的會從back開始向前一個一個比較並pop,直到遇到更有價值的) 以範測為範例講解(只模擬最大值的) 1 3 -1 -3 5 3 6 7 ``` ∣ 1 3 -1 -3 5 3 6 7 push 1進來 deque[1] ∣ 1 3 -1 -3 5 3 6 7 push 3進來 deque[1,3] 3比1大且3是新進的一定比1後面,pop 1 deque[3] ∣ ∣ 1 3 -1 -3 5 3 6 7 push -1進來 deque[3,-1] -1比較小但在後面所以留著 ans 3(最前面的) ∣ ∣ 1 3 -1 -3 5 3 6 7 push -3 pop 1(本來就不在了) deque[3,-1,-3] ans 3 3 ∣ ∣ 1 3 -1 -3 5 3 6 7 push 5 pop 3 deque[-1,-3,5] 5>3 pop -3, 5>-1 pop -1 deque[5] ans 3 3 5 ∣ ∣ 1 3 -1 -3 5 3 6 7 push 3 pop -1(本來就不在) deque[5,3] ans 3 3 5 5 ∣ ∣ 1 3 -1 -3 5 3 6 7 push 6 pop -3(本來就不在) deque[5,3,6] -> deque[5,6] ans 3 3 5 5 6 ∣ ∣ 1 3 -1 -3 5 3 6 7 push 7 pop 5(本來就不在) deque[6,7] ans 3 3 5 5 6 7 ``` 時間複雜度$O(n)$ 以剛剛那題為實作範例 說明在code裡 ```cpp= #include<bits/stdc++.h> using namespace std; #define int long long int n, k; int a[1000005]; signed main(){ios::sync_with_stdio(0);cout.tie(0); while(cin >> n >> k){ if(k > n)k = n; memset(a,0,sizeof(a)); for(int i = 0; i < n; i++){ cin >> a[i]; } deque<int> qmax;//存數字在陣列的位置 deque<int> qmin;//存數字在陣列的位置 qmax.push_back(0); qmin.push_back(0); for(int i = 1; i < n; i++){ //要小心deque是不是空的避免NA while(!qmin.empty() && qmin.front() <= i - k){qmin.pop_front();} //qmin.front() <= i - k 是判斷有沒有超出框框 while(!qmin.empty() && a[i] <= a[qmin.back()]){qmin.pop_back();} //a[i] <= a[qmin.back()] 是判斷deque內的數有是否還有價值 qmin.push_back(i); if(i >= k - 1){ cout << a[qmin.front()]; //當前框框最有價值的就是在front if(i < n - 1)cout << ' '; } }cout << '\n'; for(int i = 1; i < n; i++){ //max的和min是一樣的 while(!qmax.empty() && qmax.front() <= i - k){qmax.pop_front();} while(!qmax.empty() && a[i] >= a[qmax.back()]){qmax.pop_back();} qmax.push_back(i); if(i >= k - 1){ cout << a[qmax.front()]; if(i < n - 1)cout << ' '; } }cout << '\n'; } return 0; } ```