单调队列~~

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


单调队列和滑动窗口结合的经典题目

1.滑动窗口最大值

在这里插入图片描述

https://leetcode.cn/problems/sliding-window-maximum/description/

class Solution {
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        int n=nums.size();
        int h=0,t=0;
        int deque[100002];
        for(int i=0;i<k-1;i++){
            while(h<t && nums[deque[t-1]]<=nums[i]){
                t--;
            }
            deque[t++]=i;
        }
        int m=n-k+1;
        vector<int> ans(m);
        for(int l=0,r=k-1;l<m;l++,r++){
            while(h<t && nums[deque[t-1]]<=nums[r])t--;
            deque[t++]=r;
            ans[l]=nums[deque[h]];
            if(l==deque[h])h++;
        }
        return ans;
    }
};

2.绝对差不超过限制的最长连续子数组


https://leetcode.cn/problems/longest-continuous-subarray-with-absolute-diff-less-than-or-equal-to-limit/

典型的滑动窗口问题(不知道为啥见我滑动窗口文章的总结)
但是“满足条件”的判定有些难度,由题意我们发现需要时刻维护当前的子数组的最大值和最小值,故可以用单调队列来维护

class Solution {
public:
    int longestSubarray(vector<int>& nums, int limit) {
        int n=nums.size();
        int ans=1;
        int q1[100002],q2[100002],h1=0,h2=0,t1=0,t2=0;//q1大的在头,q2小的在头
        for(int l=0,r=0;r<n;r++){
            while(h1<t1 && nums[q1[t1-1]]<=nums[r])t1--;
            while(h2<t2 && nums[q2[t2-1]]>=nums[r])t2--;
            q1[t1++]=r,q2[t2++]=r;
            while(nums[q1[h1]]-nums[q2[h2]]>limit){
                if(l==q1[h1])h1++;
                if(l==q2[h2])h2++;
                l++;
            }
            ans=max(ans,r-l+1);
        }
        return ans;
    }
};

3.洛谷2698

在这里插入图片描述
也是典型的滑动窗口,与上题类似

#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef long long ll;
const int N = 1000020;
struct node{
    int x,y;
}m[N];
bool cmp(node aa,node bb){
    return aa.x<bb.x;
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    int n,limit;
    int ans=1e7;
    cin>>n>>limit;
    for(int i=0;i<n;i++){
        cin>>m[i].x>>m[i].y;
    }
    sort(m,m+n,cmp);
    int q1[100002], q2[100002], h1 = 0, h2 = 0, t1 = 0, t2 = 0; //q1大的在头,q2小的在头
    for(int l=0,r=0;r<n;r++){
        while(h1<t1 && m[q1[t1-1]].y<=m[r].y)t1--;
        while(h2<t2 && m[q2[t2-1]].y>=m[r].y)t2--;
        q1[t1++]=r;
        q2[t2++]=r;
        //if(r!=n-1 && m[r]==m[r+1])continue;
        if(m[q1[h1]].y-m[q2[h2]].y<limit)continue;
        while(m[q1[h1]].y-m[q2[h2]].y>=limit){
            if(l==q1[h1])h1++;
            if(l==q2[h2])h2++;
            l++;
        }
        ans=min(ans,m[r].x-m[l-1].x);
    }
    if(ans>1000000){
        cout<<"-1"<<endl;
    }else{
        cout<<ans<<endl;
    }
    return 0;
}

其他题目

1.和至少为k的最短子数组

在这里插入图片描述
题解
大多数人(包括我)乍一看惊呼“这不滑动窗口嘛,太简单啦”
但是。。。数组中存在负数,破坏了单调性,滑动窗口做不了。。。
所以,需要用到单调队列,用来维持目前情况下达成更优解的左边界的可能性
具体做法为:数组元素依次进入队列,考虑每个元素进入时,从队头开始淘汰“以该元素为右边界,以队头元素为左边界的情况下”能满足条件的元素并时刻更新最短长度,然后从队尾淘汰不比该元素小的元素,最后将该元素从队尾入队

class Solution {
public:
    long long int sum[100002];
    int q[100003];
    int shortestSubarray(vector<int>& nums, int k) {
        int n=nums.size();
        int h=0,t=1;
        for(int i=0;i<n;i++){
            sum[i+1]=sum[i]+nums[i];
        }
        int ans=1e6;
        for(int r=1;r<=n;r++){
            while(sum[r]-sum[q[h]]>=k && h<t){
                ans=min(ans,r-q[h]);
                h++;
            }
            while(h<t && sum[r]<=sum[q[t-1]])t--;
            q[t++]=r;
        }
        return (ans>100000) ? -1 : ans;
    }
};

2.满足不等式的最大值


https://leetcode.cn/problems/max-value-of-equation/description/

在这里插入图片描述

class Solution {
public:
    int findMaxValueOfEquation(vector<vector<int>>& points, int k) {
        int n=points.size();
        long long m[100002];
        int q[100002];
        int h=0,t=0;
        for(int i=0;i<n;i++){
            m[i]=points[i][1]-points[i][0];
        }
        long long ans=-1e9;
        for(int i=0;i<n;i++){
            while(h<t && points[i][0]-points[q[h]][0]>k)h++;
            if(h<t)ans=max(ans,m[q[h]]+points[i][0]+points[i][1]);
            while(h<t && m[q[t-1]]<=m[i])t--;
            q[t++]=i;
        }
        return ans;
    }
};

3.你可以安排的最多任务数目


https://leetcode.cn/problems/maximum-number-of-tasks-you-can-assign/description/

在这里插入图片描述
题目用到了二分+贪心(最重要)+单调队列(其实只是个双端队列)
二分就不讲了,就讲一下f函数的代码解释
mid代表需完成的任务数,我们考虑贪心思想,task取最小的mid个,worker选最大的mid个,然后依次从小到大考虑工人:我们考虑每个工人时,将其有能力能够完成的任务进队,然后查看队头的任务是否能被他完成,若能,则让他完成队头任务(这样才最不亏),然后考虑下一个工人;若不能,看药丸还够不够,若不够,则return 0;若够,给他吃,然后再将他现在有能力完成的任务入队,再看队中是否有任务,若没有,说明还不行,return 0;若有,则将队尾的任务出队(这样才能最大化利用药丸)

class Solution {
public:
    int q[50002];
    int maxTaskAssign(vector<int>& tasks, vector<int>& workers, int pills, int strength) {
        int n=tasks.size(),m=workers.size();
        int l=0,r=min(m,n)+1,mid;
        sort(tasks.begin(),tasks.end());
        sort(workers.begin(),workers.end());
        while(l+1<r){
            mid=(r+l)>>1;
            if(f(mid,tasks,workers,pills, strength))l=mid;
            else r=mid;
        }
        return l;
    }
    bool f(int mid,vector<int> tasks, vector<int> workers,int pills, int strength){
        int m=workers.size();
        vector<bool> st(mid);
        int h=0,t=0,tmp=0;
        for(int i=m-mid;i<m;i++){
            int s=workers[i];
            while(tmp<mid && tasks[tmp]<=s)q[t++]=tasks[tmp++];
            if(h<t && q[h]<=s){
                h++;
                continue;
            }
            if(pills){
                pills--;
                s+=strength;
                while(tmp<mid && tasks[tmp]<=s)q[t++]=tasks[tmp++];
                if(h==t)return 0;
                t--;
            }else{
                return 0;
            }
        }
        return 1;
    }
};

总结

单调队列往往是和其他算法结合的,它本身的算法思想其实不多。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值