提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
单调队列~~
单调队列和滑动窗口结合的经典题目
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;
}
};
总结
单调队列往往是和其他算法结合的,它本身的算法思想其实不多。