单调队列的性质:
(1):队列中的元素是单调的
(2):队尾插入元素,队首队尾删除元素
应用:求静态区间最值
裸单调队列
Description
An array of size n ≤ 10 6 is given to you. There is a sliding window of size k which is moving from the very left of the array to the very right. You can only see the k numbers in the window. Each time the sliding window moves rightwards by one position. Following is an example:
The array is [1 3 -1 -3 5 3 6 7], and k is 3.
Window position Minimum value Maximum value
[1 3 -1] -3 5 3 6 7 -1 3
1 [3 -1 -3] 5 3 6 7 -3 3
1 3 [-1 -3 5] 3 6 7 -3 5
1 3 -1 [-3 5 3] 6 7 -3 5
1 3 -1 -3 [5 3 6] 7 3 6
1 3 -1 -3 5 [3 6 7] 3 7
Your task is to determine the maximum and minimum values in the sliding window at each position.
Input
The input consists of two lines. The first line contains two integers n and k which are the lengths of the array and the sliding window. There are n integers in the second line.
Output
There are two lines in the output. The first line gives the minimum values in the window at each position, from left to right, respectively. The second line gives the maximum values.
Sample Input
8 3
1 3 -1 -3 5 3 6 7
Sample Output
-1 -3 -3 -3 3 3
3 3 5 5 6 7
题意:n个数,窗口长度为k,求[i,i+k-1]中的最大值和最小值。
做法:以最大值为例,维护一个单调递减的队列,先把前k个数入队,然后遍历k到n的数,分别维护队尾和队首,维护队尾是保证队列的单调性,维护队首是保证队首元素的下标不小于i-k+1,因为队首元素代表[i-k+1,i]这个区间的最大值
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <vector>
using namespace std;
typedef long long ll;
const int maxn = 1000005;
int a[maxn],mi[maxn],mx[maxn],n,k;
struct node
{
int x,y;
}q[maxn];
void Getmin()
{
int i,head = 1,tail = 0;
for(i=1; i<k; i++)
{
while(head <= tail && q[tail].x >= a[i])//队列不为空且队尾元素大于等于当前元素就要出队,直到队列为空或者队尾元素小于当前元素
tail--;
q[++tail].x = a[i];
q[tail].y = i;
}
for(;i<=n; i++)
{
while(head <= tail && q[tail].x >= a[i])//维护队尾
tail--;
q[++tail].x = a[i];
q[tail].y = i;
while(q[head].y < i-k+1)//维护队首
head++;
mi[i-k+1] = q[head].x;
}
}
void Getmax()
{
int i,head = 1,tail = 0;
for(i=1; i<k; i++)
{
while(head <= tail && q[tail].x <= a[i])
tail--;
q[++tail].x = a[i];
q[tail].y = i;
}
for(;i<=n; i++)
{
while(head <= tail && q[tail].x <= a[i])
tail--;
q[++tail].x = a[i];
q[tail].y = i;
while(q[head].y < i-k+1)
head++;
mx[i-k+1] = q[head].x;
}
}
int main()
{
scanf("%d%d",&n,&k);
for(int i=1; i<=n; i++)
{
scanf("%d",&a[i]);
}
Getmin();
Getmax();
for(int i=1; i<=n-k+1; i++)
{
printf("%d ",mi[i]);
}
printf("\n");
for(int i=1; i<=n-k+1; i++)
{
printf("%d ",mx[i]);
}
}
单调队列的灵活应用
Description
Pxt每过一段时间都会更换各个社交账号的密码。密码当然不能明文存储,Pxt随意写下了若干个n位16进制整数,一则k位的密码是将该数保留k位且相对位置不变的最大16进制数。
Pxt现在要去睡午觉,请你帮助他完成密码生成器。
Standard Input
包含多组测试数据。
每组测试数据,第一行是两个整数n,k,含义如题目所示。
第二行包含一个n位16进制整数,字母小写,n位数以空格分隔。
Standard Output
每组数据输出一行表示Pxt的k位密码。(不含空格)
Samples
Input
4 2
9 a b c
6 3
1 a 2 b 3 c
Output
bc
b3c
Constraints
n,k≤100,000n,k≤100,000
Note
数据加强,注意数据范围 存在k=0
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <queue>
using namespace std;
const int maxn = 100005;
char s[maxn],t[maxn];
int main()
{
int n,k;
while(scanf("%d%d",&n,&k) != EOF)
{
for(int i=0; i<n; i++)
{
cin>>s[i];
}
int i;
deque<char>q;
for(i=0; i<n-k+1; i++)
{
while(!q.empty() && s[i] > q.front())
q.pop_front();
q.push_front(s[i]);
}
int j = 0;
t[j++] = q.back();
q.pop_back();
for(; i<n; i++)
{
while(!q.empty() && s[i] > q.front())
q.pop_front();
q.push_front(s[i]);
t[j++] = q.back();
q.pop_back();
}
for(int i=0; i<k; i++)
{
printf("%c",t[i]);
}
printf("\n");
}
}
描述:
老板需要你帮忙浇花。给出N滴水的坐标,y表示水滴的高度,x表示它下落到x轴的位置。
每滴水以每秒1个单位长度的速度下落。你需要把花盆放在x轴上的某个位置,使得从被花盆接着的第1滴水开始,到被花盆接着的最后1滴水结束,之间的时间差至少为D。
我们认为,只要水滴落到x轴上,与花盆的边沿对齐,就认为被接住。给出N滴水的坐标和D的大小,请算出最小的花盆的宽度W。
输入输出格式
输入格式:
第一行2个整数 N 和 D。
第2… N+1行每行2个整数,表示水滴的坐标(x,y)。
输出格式:
仅一行1个整数,表示最小的花盆的宽度。如果无法构造出足够宽的花盆,使得在D单位的时间接住满足要求的水滴,则输出-1。
输入样例#1:
4 5
6 3
2 4
4 10
12 15
输出样例#1:
2
题意:给出n个点的x、y值,找最小的区间(区间长度为端点的x值相减),使得区间上的点的y值的最大值和最小值的差大于等于D。
做法:尺取+单调队列维护区间最值。一个单调队列只能维护区间的最大值或最小值,所以要用两个单调队列来维护区间最值。单调递增队列的队首是区间最小值,单调递减队列的队首的区间最大值。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <stack>
#include <queue>
using namespace std;
const int maxn = 1e6+5;
const int INF = 0x3f3f3f3f;
struct node
{
int x,y,id;
}a[maxn];
bool cmp(node a,node b)
{
if(a.x != b.x)
return a.x < b.x;
else
return a.y < b.y;
}
int main()
{
int n,d;
scanf("%d%d",&n,&d);
for(int i=1; i<=n; i++)
scanf("%d%d",&a[i].x,&a[i].y);
sort(a+1,a+1+n,cmp);
for(int i=1; i<=n; i++)
a[i].id = i;
deque<node> q1,q2;
int r,ans = INF;
q1.push_back(a[1]);
q2.push_back(a[1]);
r = 1;
for(int i=1;i<=n;i++) {
while(q1.front().id<i) q1.pop_front();
while(q2.front().id<i) q2.pop_front();//这两句相当于枚举左端点
while(r<n&&q2.front().y-q1.front().y<d) {//右端点不符合条件就一直++
r++;
while(!q2.empty()&&q2.back().y<=a[r].y) {
q2.pop_back();
}
q2.push_back(a[r]);
while(!q1.empty()&&q1.back().y>=a[r].y) {
q1.pop_back();
}
q1.push_back(a[r]);
}
if(q2.front().y-q1.front().y<d) break;
ans=min(ans,a[r].x-a[i].x);
}
if(ans==INF) puts("-1"); else
printf("%d\n",ans);
}