[单调队列]

单调队列

特点

是一种主要用于解决 滑动窗口 类问题的数据结构

在长度为 \(n\) 的序列中,求每个长度为 \(k\) 的区间的区间最值,队头为区间最值

时间复杂度是 \(O(n)\) 在这个问题中比\(O(nlogn)\)的ST表和线段树要优

思想

单调队列故队列里面的数是单调递增或单调递减的,如果维护的是区间最小,则应为单调递增,那么考虑将一个数放入队列的时候,如果它比前面的大,那么就直接\(++tail\)然后\(q[tail]=a[i]\),如果它小于等于前面的数,那么就把前面的数移除,因为在这个区间要维护最小值,比它大的就没有意义了,故应该\(tail--\)直到它比前面的大或者队列为空

模板题滑动窗口

#include <cstdio>
#include <iostream>
using namespace std;
const int maxn = 1e6+7;
int n,k,ans[maxn],a[maxn];
int head,tail;
int Head,Tail;
struct qqq{
	int value,pos;
}qmin[maxn],qmax[maxn];

int main()
{
	scanf("%d%d",&n,&k);
	for(int i=1;i<=n;i++)
		scanf("%d",&a[i]);
	for(int i=1;i<=n;i++)
	{
		while(a[i] >= qmax[Tail].value && Tail >= Head) Tail --;//如上所述
		while(a[i] <= qmin[tail].value && tail >= head) tail --;
		qmax[++Tail].value = a[i];qmax[Tail].pos = i;
		qmin[++tail].value = a[i];qmin[tail].pos = i;
		while(i - qmax[Head].pos >= k) Head ++;//因为是求长度为k区间的最值,故队列长度不应该超过k
		while(i - qmin[head].pos >= k) head ++;
		if(i >= k)
		{
			printf("%d ",qmin[head].value);
			ans[i] = qmax[Head].value;//要先输出小的,再输出大的,故存一下答案
		}
	}
	printf("\n");
	for(int i=k;i<=n;i++)
		printf("%d ",ans[i]);
	return 0;
}

洛谷P1725单调队列+dp

就是第\(i\)个格子可以由之前的\([i-R,i-L]\)来转移得到,故状态转移方程用一个单调队列来维护最大值即可

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int maxn = 2e5+6,inf = 2147483647;
int n,pos[maxn],L,R,dp[maxn];
int maxi = -inf,a[maxn];
int Head,Tail;
int main()
{
	memset(dp,128,sizeof(dp));//128自然溢出为最小值,因为有负数所以要初始化为负无穷
	scanf("%d%d%d",&n,&L,&R);
	for(int i=0;i<=n;i++)
		scanf("%d",&a[i]);
	dp[0] = 0;//题目要求0处值为0
	for(int i=L;i<=n;i++)//因为从0开始跳,最先能到达的位置也是L,故从L开始循环
	{
		while(dp[i-L] >= dp[pos[Tail]] && Tail >= Head) Tail --;
		pos[++Tail] = i-L;
		while(i - pos[Head] > R) Head ++;
		dp[i] = dp[pos[Head]] + a[i];//区间最大值即为pos[Head]所在位置的值
		if(i >= n-R+1)//从这个位置才能跳过终点
			maxi = max(maxi,dp[i]);
	}
	printf("%d",maxi);
	return 0;
}
posted @ 2024-03-05 09:55  风丨铃  阅读(5)  评论(0编辑  收藏  举报