有关单调队列
想要理解算法,最有效的方式就是看例题了。
所以我们就看这道模板题
P1886 滑动窗口 /【模板】单调队列
刚开始我还不知道单调队列是个什么东西,但是大概知道要用一个双向的队列来写,于是就想到了multiset+queue。
我们首先把数字一个一个放到队列和multiset中,当我们发现队列的size等于k时,就可以将multiset中的第一个数和最后一个分别放到两个个记录数组中,一个是最小值一个是最大值,
然后二分找到muliset中队列的第一个数删去,再把队列第一个数pop掉就ok了,时间复杂度大概是nlogn。
然而这样的时间复杂度并不太行,t掉两个点只有80分,下面是代码。
#include<iostream> #include<set> #include<queue> using namespace std; multiset<int>q; queue<int> p; int minn[1000005], maxx[1000005], a = 0; int main() { int n, m; cin >> n >> m; for (int i = 1; i <= n; i++) { int now; cin >> now; p.push(now); q.insert(now); if (p.size() == m) { a++; minn[a] = *q.begin(); auto b = q.end(); b--; maxx[a] = *b; b = q.lower_bound(p.front()); q.erase(b); p.pop(); } } for (int i = 1; i <= a; i++) cout << minn[i] << " "; cout << endl; for (int i = 1; i <= a; i++) cout << maxx[i] << " "; return 0; }
之后没有办法,就只好去学习了一下单调队列。
单调队列跟优先队列很像,都是队列中的元素要么单调递增要么单调递减,但是单调队列可以对两头进行操作。
由于stl当中没有单调队列,我们就只能手打(胡说明明有deque)。
对于这题来说,其实就是要我们维护一个大小为k的两个单调队列,一个单调递增,一个单调递减。
对于单调递增队列来说,我们只要遇到一个比队列中最后一个数还要小的数,那么我们就一直从队尾出队直到这个数比队尾的数大,并将其加入队列中。
而单调递增则相反不必赘述,下面直接上代码就好了。
#include<iostream> using namespace std; int a[1000005], q1[1000000], q2[1000005], n, k,book[1000005]; void maxx() { int tail = 0; int head = 1; for (int i = 1; i <= n; i++) { while (head <= tail && a[i] >= q2[tail])tail--; q2[++tail] = a[i]; book[tail] = i; while ( book[head] <= i - k) head++; if (i >= k)cout << q2[head] << " "; } } void minn() {//求窗口中最小的,其实是求一个单调递增队列,这样每次输出它的头部,那就必是最小的了 int tail = 0; int head = 1; for (int i = 1; i <= n; i++) { while (head <=tail && a[i] <= q1[tail])tail--;//遇到比队尾小的,我们就一直出队,直到大于队尾为止 q1[++tail] = a[i]; book[tail] = i; while (book[head] <= i - k) head++; if (i >= k)cout << q1[head] << " "; } cout << endl; } int main() { cin >> n >> k; for (int i = 1; i <= n; i++) cin >> a[i]; minn(); maxx(); return 0; }
我们这里采用了个手写队列。
手写队列时要将头节点初始化为1,尾节点初始化为0,具体为什么讲不清楚,但这样才能实现队列的操作。

 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号