单调队列
单调队列的核心在于已 \(O(n)\) 的时间复杂度完成扫描,即每个元素最多一次入队一次出队。在有单调性(区间越长答案更优)的最优解区间内问题中,显然的,当后续的元素比前方任意一个元素优,前方的这个元素就应该被淘汰了。但是如果不如前方元素,则有机会等前方元素被刷掉再成为最优。故每次更新后,刷掉区间最早入队的已经离开区间的元素,刷掉最后入队却不如即将入队的元素的元素即可。
滑动窗口 /【模板】单调队列
思路
简单的版题。
\(Code\)
#include<bits/stdc++.h>
using namespace std;
int n, k;
int a[1000005];
int minn[1000005], maxn[1000005];
template<typename T>
inline void read(T&x){
x = 0; char q; bool f = 1;
while(!isdigit(q = getchar())) if(q == '-') f = 0;
while(isdigit(q)){
x = (x<<1) + (x<<3) + (q^48);
q = getchar();
}
x = f?x:-x;
}
template<typename T>
inline void write(T x){
if(x < 0){
putchar('-');
x = -x;
}
if(x > 9) write(x/10);
putchar(x%10^48);
}
int main(){
read(n), read(k);
for(register int i = 1; i <= n; ++i) read(a[i]);
int front = 1, end = 0;
for(register int i = 1; i <= n; ++i){
while(front <= end && minn[front]+k <= i) ++front;
while(front <= end && a[minn[end]] >= a[i]) --end;
minn[++end] = i;
if(i >= k){
write(a[minn[front]]), putchar(' ');
}
}
puts("");
front = 0, end = 0;
for(register int i = 1; i <= n; ++i){
while(front <= end && maxn[front]+k <= i) ++front;
while(front <= end && a[maxn[end]] <= a[i]) --end;
maxn[++end] = i;
if(i >= k){
write(a[maxn[front]]), putchar(' ');
}
}
puts("");
return 0;
}
[USACO12MAR] Flowerpot S
思路
这是一道比较有思维含量的一道题。首先对于最大最小值的查找很容易想到单调队列,但是由于区间长度是不定的,如果枚举区间长度时间复杂度为 \(O(n^2)\),显然是不可接受的。于是顺理成章的想到了由于不同大小的区间所得到的答案是长度越长答案越大,即单调的,可以考虑二分答案并用单调队列实现答案的判断,时间复杂度为 \(O(n\log n)\),已经可以通过该题。但是可以考虑到的是,如果以一个左端点为起始,那么向右移动过程中,\(\max-\min\) 的差值也是单调递增的,也就是找到第一个符合要求的答案后,只需要移动 \(l\),并重新在 \(r\) 向右查找新的符合题意的 \(r\) 即可。
\(Code\)
#include<bits/stdc++.h>
using namespace std;
int n, d;
struct node{
int x, y;
}a[100005];
int minn[100005], maxn[100005];
template<typename T>
inline void read(T&x){
x = 0; char q; bool f = 1;
while(!isdigit(q = getchar())) if(q == '-') f = 0;
while(isdigit(q)){
x = (x<<1) + (x<<3) + (q^48);
q = getchar();
}
x = f?x:-x;
}
template<typename T>
inline void write(T x){
if(x < 0){
putchar('-');
x = -x;
}
if(x > 9) write(x/10);
putchar(x%10^48);
}
inline bool cmp(node a, node b){
return a.x < b.x;
}
int main(){
read(n), read(d);
for(register int i = 1; i <= n; ++i) read(a[i].x), read(a[i].y);
sort(a+1, a+n+1, cmp);
int ans = 0x3f3f3f3f, front1 = 1, front2 = 1, end1 = 0, end2 = 0; //两个双端队列,分别储存max,min
for(register int l = 1, r = 0; l <= n; ++l){
while(front1 <= end1 && minn[front1] < l) ++front1; //左端点超出范围的区间不再保存
while(front2 <= end2 && maxn[front2] < l) ++front2;
while(a[maxn[front2]].y-a[minn[front1]].y < d && r < n){ //不断向右查找答案直到符合题意
++r;
while(front1 <= end1 && a[minn[end1]].y >= a[r].y) --end1; //维护单调队列
minn[++end1] = r;
while(front2 <= end2 && a[maxn[end2]].y <= a[r].y) --end2;
maxn[++end2] = r;
}
if(a[maxn[front2]].y-a[minn[front1]].y >= d) ans = min(ans, a[r].x-a[l].x); //判断是否这个l有对应的r满足题意
}
if(ans == 0x3f3f3f3f) puts("-1"); //判断有无解
else write(ans);
return 0;
}