计算满足最大值减最小值小于某个数的子数组个数

计算满足最大值减最小值小于某个数的子数组个数_子数组

分析:

先验知识: 

一个数组中,如果满足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;
}

 

计算满足最大值减最小值小于某个数的子数组个数_最小值_02