有关单调队列

想要理解算法,最有效的方式就是看例题了。

所以我们就看这道模板题 

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,具体为什么讲不清楚,但这样才能实现队列的操作。

posted @   redintonc  阅读(57)  评论(0编辑  收藏  举报
编辑推荐:
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
阅读排行:
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
点击右上角即可分享
微信分享提示