给定n个数,求一段长度为K的区间s.t.这段区间内的最大值减去最小值最大
这是一道经典的 单调队列
假设 数列 1~n 暴力就是枚举所有长度为K的区间 复杂度显然是O(NK)的
我们在暴力的时候会顺序地枚举区间
观察发现第i个区间和第i+1 的区间只差了2个数
我们每次重新枚举 们浪费时间
我们就需要一个数据结构
能支持一个插入和删除操作,而且能维护区间的最大值和最小值
当然线段树可以使用
这道题有点大材小用
void dandiao(){ //我们不仅要保证数列是单调的而且还要保证所有数在原来数组中的位置也是单调的
int front = 1,tail = 0; //所以我们不把所有的数装进去
for(int i = 1;i <= k;i++){
while(tail >= front && z[a] < q[tail]) { //pd 队列是否为空
tail--;
}
q[++tail] = z[a]; //单调队列即可以从队尾出也可以从队首出<queue>从队尾出不方便
} //这是一个单增的单调队列但是有些数已经被pop掉了
//显然的队列的第一个元素就有最小值,因为如果你插入一个很小的数
// 单调队列会弹出前方所有的元素 所以我们可以求出前k个数的最大最小值
// 那么我们加一个数是很好加的 那么怎么删呢
//我们不妨记录一个各个元素的位置
for(int i = k + 1;i <= n;i++) {
while(tail >= front && z[a] < q[tail]) {
tail++;
}
q[tail][0] = z[a]; q[taik][1] = a;
if(q[front][1] == a-k) front++; //这是个很优秀的O(n)的做法
//每个元素最多被加进去一次再弹出来一次
}
}
如果我们改一下这个题目,把问题改成最大值减去最小值不超过k的数长度最大呢?
如果我们找到一个长度为l的区间满足,那么我们一定可以找到一个区间l' 他也满足这个条件
或换句话说,我们可以用二分答案,枚举区间l只要他满足上面的条件,那么答案一定>= l
复杂度显然是O(nlogn)的
我们还有一个更好的线性的做法
我们考察在这个题目中区间从某一个区间移动到某一个区间的变化
我们要使得这个区间最长,我们完全不用考虑不长的
例如我们固定一个左端点1 我们可以通过暴力求出这个点最远可以到哪里
当然我们所有的点都可以暴力 那么答案的区间一定在这些个区间中的一个
我们继续考察从第一个区间移动到第二个区间的变化
显然的左边会删掉一个数,但右边一定不会删掉数!
所以右端点是一个不完全严格递增的
所以一个一个加就好了
所以枚举左端点后右端点就可以直接出来了 所以每个数只能被push pop 个一次
线性做法就结束了