计算满足最大值减最小值小于某个数的子数组个数
分析:
先验知识:
一个数组中,如果满足max(arr[i..j])-min(arr[i..j]),则内部的所有子数组都满足 (仔细思考一下)
一个数组中,如果不满足max(arr[i..j])-min(arr[i..j]),则内部的所有子数组都不满足
用双指针的方式,i和j都从左往右移动,但是i不能超过j
当i和j范围内都满足条件,则 j 往右移动,i不动。
如果到达j+1 时不满足了,则计算以i为端点的所有子数组。即为 j-i+1。并且,i++。
其中需要用2个双端队列维护这个范围内的最大值和最小值。
并且满足,双端队列里的值中,
最大值双端队列是以 从大到小的顺序,这样保证对头是最大值,每次取出只要O(1),
最小值双端队列是以 从小到大的顺序,这样保证对头是最小值,每次取出只要O(1),
注意这里存的是下标,因为 还要根据下标判断 这个 双端队里里的值是否失效。 因为 i 和 j 都会移动。
代码:
#include<iostream>
#include<cstdlib>
#include<deque>
using namespace std;
int arr[1000];
int cal_fun(int *arr, int N,int M){
int ans = 0;
//最大双端队列
deque<int> max_que;
//最小双端队列
deque<int> min_que;
int i,j;
i=j=0;
while(i<N){
while(j<N){
//如果当前值比队尾的数大或相等,则不断弹出 维持从大到小的队列
while(!max_que.empty() && arr[max_que.back()]<=arr[j])max_que.pop_back();
max_que.push_back(j);
//如果当前值比队尾的数小或相等,则不断弹出 维持从小到大的队列
while(!min_que.empty() && arr[min_que.back()]>=arr[j])min_que.pop_back();
min_que.push_back(j);
//直接从队头取出最大 值 和 最小值
int max_ = arr[max_que.front()];
int min_ = arr[min_que.front()];
//如果不满足条件,则i需要往前走
if(max_-min_>M)break;
j++;
}
ans+= j-i;
i++;
//如果当前左指针已经超过 最大或最小队头的下标,则该数失效,需要将其弹出
while(!max_que.empty() && i>max_que.front()) max_que.pop_front();
while(!min_que.empty() && i>min_que.front()) min_que.pop_front();
}
return ans;
}
int main(){
int N;
scanf("%d",&N);
for(int i=0;i<N;i++){
scanf("%d",&arr[i]);
}
int M;
scanf("%d",&M);
int ans = cal_fun(arr,N,M);
printf("%d\n",ans);
return 0;
}