给定数组 arr 和整数 num,共返回有多少个子数组满足如下情况:
max(arr[i...j]) - min(arr[i...j]) <= num
max(arr[i...j])表示子数组arr[i...j]中的最大值,min[arr[i...j])表示子数组arr[i...j]中的最小值。
第一行输入两个数 n 和 num,其中 n 表示数组 arr 的长度
第二行输入n个整数,表示数组arr中的每个元素
输出给定数组中满足条件的子数组个数
5 2 1 2 3 4 5
12
//详情请见左神书籍,这个算法碉堡了 //我针对一些代码写些注释方便理解 #include<iostream> #include<vector> #include<deque> using namespace std; int main() { long long n; long long num; cin >> n >> num; vector<long long> datas(n, 0); for (int i = 0; i<n; i++) { long long x; cin >> x; datas[i] = x; } //以上用于输入 deque<int> dq1;//最大值队列 deque<int> dq2;//最小值队列 int res = 0; int j = 0; int i = 0; for (; i<datas.size(); i++) { //以i为起始位置的子数组 for (; j<datas.size(); j++) { while (!dq1.empty() && datas[j] >= datas[dq1.back()]) dq1.pop_back(); dq1.push_back(j); while (!dq2.empty() && datas[j] <= datas[dq2.back()]) dq2.pop_back(); dq2.push_back(j); //如果最大值减去最小值大于num就break,此时最大值 //减去最小值大于num一定是由于j的插入 if (datas[dq1.front()] - datas[dq2.front()]>num) break; } if (dq1.front() == i)//如果队头是i位置的元素,i加一后就过期删掉 dq1.pop_front(); if (dq2.front() == i) dq2.pop_front(); res += j - i;//以i为开头的小于num的子数组长度j-i } cout <<res <<endl; }
自己实现的时候注意使用下标比较还是使用值在比较,不要搞混。
import java.util.*; public class Main{ public static void main(String[] args){ Scanner sc = new Scanner(System.in); int n = sc.nextInt(); int num = sc.nextInt(); int[] arr = new int[n]; for(int i = 0; i < n; i++) { arr[i] = sc.nextInt(); } int res = getArrayNum(arr, num); System.out.println(res); } //两个结论:1.到j不满足条件,a[i..j-1]满足条件所以a[m..n]都满足条件(i<=m<=n<=j-1) //2.a[i..j]不满足条件,则包含它的也都不满足条件(a[l..k],l<=i<=j<=k) //用a[i..j-1]代入条件:max(a[i..j])-min(a[i..j])<=num里进行归纳 public static int getArrayNum(int[] arr, int num) { //判空 if(arr == null || arr.length == 0){ return 0; } //拿LinkedList作为双端队列来使用 LinkedList qmax = new LinkedList(); LinkedList qmin = new LinkedList(); //j是不重置的,因为上面两个结论,到j不满足条件时,j是不变的,所以j只会不断地增长 //j不重置则使用while语句 int i = 0; int j = 0; int result = 0; //到j不满足条件时,i..j-1所有的子数组都满足条件,但是计数的时候只计算了从i开头的 //后面剩下的会到它们为开头的时候计算(如i+1),这也是j不重置的原因 while(i < arr.length){ while(j < arr.length){ //为了保证同一个下标只进栈一次,出栈一次,这样时间复杂度才能保证(每个元素O(1),n个元素O(n)) //如果break,j不变,而qmin.peekLast()正好是上一轮的j,后面i++,所以判断[i+1..j]是否满足条件 //到j不满足条件,所以[i+1..j]不一定满足条件 if(qmin.isEmpty() || qmin.peekLast() != j){ //双端队列结构 while(!qmax.isEmpty() && arr[j] >= arr[qmax.peekLast()]){ qmax.pollLast(); } qmax.addLast(j); while(!qmin.isEmpty() && arr[j] <= arr[qmin.peekLast()]){ qmin.pollLast(); } qmin.addLast(j); } if(arr[qmax.peekFirst()] - arr[qmin.peekFirst()] > num){ break; } j++; } //以i开头一共j-i个,a[i..i]到a[i..j-1] result += (j - i); //开头i要自增,应该把队列中的i移除,只可能在最大和最小地方出现,要不就提前被弹出了 if(qmax.peekFirst() == i){ qmax.pollFirst(); } if(qmin.peekFirst() == i){ qmin.pollFirst(); } i++; } return result; } }
import java.util.LinkedList; import java.util.Scanner; public class Main { public static int getNum(int[] arr, int num) { if (arr == null || arr.length == 0) return 0; LinkedList<Integer> qmin = new LinkedList<>(); LinkedList<Integer> qmax = new LinkedList<>(); int i = 0, j = 0, res = 0; while (i < arr.length) { // 求以arr[i]为开头,满足条件最长的子序列 while (j < arr.length) { if (qmin.isEmpty() || qmin.peekLast() != j) { while (!qmin.isEmpty() && arr[qmin.peekLast()] >= arr[j]) { qmin.pollLast(); } qmin.addLast(j); while (!qmax.isEmpty() && arr[qmax.peekLast()] <= arr[j]) { qmax.pollLast(); } qmax.addLast(j); } // 当不再满足题意,max(arr[i...j] - min(arr[i...j]) <= num 退出当前循环 if (arr[qmax.getFirst()] - arr[qmin.getFirst()] > num) break; j++; } // 以arr[i]为第一个元素的子数组,满足条件的j-i个 res += j - i; // 下一次从i+1开始,删除队列里不再框内的元素arr[i] if (qmin.peekFirst() == i) qmin.pollFirst(); if (qmax.peekFirst() == i) qmax.pollFirst(); i++; } return res; } public static void main(String args[]) { Scanner sc = new Scanner(System.in); int n = sc.nextInt(); int num = sc.nextInt(); int[] arr = new int[n]; for (int i = 0; i < n; i++) { arr[i] = sc.nextInt(); } int res = getNum(arr, num); System.out.println(res); } }
#include<bits/stdc++.h> using namespace std; int getNum(vector<int>& arr, int num) { int n = arr.size(); deque<int> qmin, qmax; int i=0, j=0, res=0; while(i<n) { while(j<n) { while(!qmin.empty() && arr[qmin.back()]>=arr[j]) { qmin.pop_back(); } qmin.push_back(j); while(!qmax.empty() && arr[qmax.back()]<=arr[j]) { qmax.pop_back(); } qmax.push_back(j); if(arr[qmax.front()] - arr[qmin.front()] > num) break; j++; } if(qmin.front()==i) qmin.pop_front(); if(qmax.front()==i) qmax.pop_front(); res += j - i; i++; } return res; } int main() { int n, num; cin>>n>>num; vector<int> arr(n, 0); for(int i=0; i<n; i++) { cin>>arr[i]; } cout<< getNum(arr, num); return 0; }
#include <bits/stdc++.h> using namespace std; int main(){ int n, m; cin>>n>>m; int a[n], cnt=n; for(int i=0;i<n;i++) cin>>a[i]; for(int i=0,j=1,l=0,r=0, Max, Min;i<n-1;i++){ if(l<=i || r<=i){ Max = Min = a[i]; l = r = i; for(j=i+1;j<n;j++){ if(a[j]>Max){ Max = a[j]; l = j; } if(a[j]<Min){ Min = a[j]; r = j; } if(Max-Min>m) break; } } cnt += j-i-1; } cout<<cnt<<endl; return 0; }
// golang 版本的,用 gods 库提供的双链表import ( "fmt" dll "github.com/emirpasic/gods/lists/doublylinkedlist" )
func getArrayNum(nums []int, k int) int { qMax := dll.New() qMin := dll.New() res := 0 i, j := 0, 0 for ; i < len(nums); i++ { for j < len(nums) { preMax, _ := qMax.Get(qMax.Size() - 1) for qMax.Size() > 0 && nums[preMax.(int)] <= nums[j] { qMax.Remove(qMax.Size() - 1) preMax, _ = qMax.Get(qMax.Size() - 1) } qMax.Append(j) preMin, _ := qMin.Get(qMin.Size() - 1) for qMin.Size() > 0 && nums[preMin.(int)] >= nums[j] { qMin.Remove(qMin.Size() - 1) preMin, _ = qMin.Get(qMin.Size() - 1) } qMin.Append(j) preMax, _ = qMax.Get(0) preMin, _ = qMin.Get(0) if nums[preMax.(int)]-nums[preMin.(int)] > k { break } j++ } qq, _ := qMin.Get(0) mm, _ := qMax.Get(0) if qq.(int) == i { qMin.Remove(0) } if mm.(int) == i { qMax.Remove(0) } res += j - i } return res }
package main import ( "fmt" ) /** 窗口内最大值或最小值更新结构的实现 假设一个固定大小为W的窗口,依次划过arr, 返回每一次滑出状况的最大值 例如,arr = [4,3,5,4,3,3,6,7], W = 3 返回:[5,5,5,4,6,7] */ func maxSlidingWindow(nums []int, k int) []int { r := 0 res := []int{} queue := []int{} for r < len(nums) { //当前元素大于等于队列尾部元素,弹出队尾元素, 直到队列为空或者队尾元素大于当前元素 for len(queue) != 0 && nums[r] >= nums[queue[len(queue)-1]] { queue = queue[:len(queue)-1] } //加入队尾, 队列中存放的是索引 queue = append(queue, r) for r-k+1 > queue[0] { queue = queue[1:] } //前 k - 1 个数不用加入结果 if r >= k-1 { res = append(res, nums[queue[0]]) } r++ } return res } /** 给定一个整型数组arr,和一个整数num 某个arr中的子数组sub,如果想达标,必须满足:sub中最大值 – sub中最小值 <= num, 返回arr中达标子数组的数量 5 2 1 2 3 4 5 */ func countSubArray(arr []int, num int) int { if len(arr) == 0 { return 0 } ans := 0 l, r := 0, 0 minW, maxW := []int{}, []int{} for l < len(arr) { // r 往右扩, 遇到第一个不达标的停止 // 计算以 l 开始的子数组个数 for r < len(arr) { for len(maxW) != 0 && arr[maxW[len(maxW)-1]] <= arr[r] { maxW = maxW[:len(maxW)-1] } maxW = append(maxW, r) for len(minW) != 0 && arr[minW[len(minW)-1]] >= arr[r] { minW = minW[:len(minW)-1] } minW = append(minW, r) if arr[maxW[0]]-arr[minW[0]] <= num { r++ } else { break } } for len(maxW) > 0 && maxW[0] <= l { maxW = maxW[1:] } for len(minW) > 0 && minW[0] <= l { minW = minW[1:] } ans += r - l l++ } return ans } func main() { var n, num int fmt.Scan(&n, &num) arr := []int{} for i := 1; i <= n; i++ { var t int if cc, _ := fmt.Scan(&t); cc == 0 { break } arr = append(arr, t) } count := countSubArray(arr, num) fmt.Println(count) }
#include "bits/stdc++.h" /* 方法1:复杂度O(n*n) 方法2:即用双向单调队列求窗口最大值和最小值 */ using namespace std; int main() { int len;cin>>len; int num;cin>>num; int ret=0; vector<int> vec(len); for(int i=0;i<len;i++)cin>>vec[i]; /*for(int i=0;i<len;i++) { int max_num=vec[i]; int min_num=vec[i]; for(int j=i;j<len;j++) { max_num=max(max_num,vec[j]); min_num=min(min_num,vec[j]); if(max_num-min_num<=num) ret++; else break; } }*/ deque<int> stk_max; deque<int> stk_min; //int flag=1; int i=0,j=0; while(i<len) { while(j<len) { if(stk_min.empty()||stk_min.back()!=vec[j]) { while(!stk_max.empty()&&stk_max.back()<vec[j]){stk_max.pop_back();} stk_max.push_back(vec[j]); while(!stk_min.empty()&&stk_min.back()>vec[j]){stk_min.pop_back();} stk_min.push_back(vec[j]); } //else if() if(stk_max.front()-stk_min.front()>num) {break;} j++;//flag=1; //cout<<j<<' '; } if(stk_max.front()==vec[i])stk_max.pop_front(); if(stk_min.front()==vec[i])stk_min.pop_front(); ret+=j-i;//表示,以i开头的子数组 i++; //flag=0; //cout<<j<<' '<<i<<' '<<ret<<endl; } cout<<ret; return 0; }
#include <bits/stdc++.h> using namespace std; int main(){ int n, num; scanf("%d%d", &n, &num); vector<int> v(n, 0); for(int i = 0; i < n; i++) scanf("%d", &v[i]); deque<int> qmax, qmin; int res = 0; int i = 0, j = 0; qmax.push_back(0); qmin.push_back(0); while(i < n){ while(j < n && v[qmax.front()] - v[qmin.front()] <= num){ j++; while(!qmax.empty() && v[qmax.back()] < v[j]) qmax.pop_back(); qmax.push_back(j); while(!qmin.empty() && v[qmin.back()] > v[j]) qmin.pop_back(); qmin.push_back(j); } res += j - i; i++; if(qmax.front() < i) qmax.pop_front(); if(qmin.front() < i) qmin.pop_front(); } printf("%d\n", res); return 0; }
今天吃火龙果
let qmax=[],qmin=[] let i=0,j=0 while(j<line.length&&i<line.length){ // j一定是极值进栈才会i右移 //如果极值进栈则先处理前面的 先不让极值进栈 while(qmin.length>0&&qmax.length>0&&((line[j]<line[qmin[0]]&&line[qmax[0]]-line[j]>n) ||(line[j]>line[qmax[0]]&&line[j]-line[qmin[0]]>n))){ res+=j-i if(qmin[0]==i)qmin.shift() if(qmax[0]==i) qmax.shift() i++ } while(qmin.length>0 && line[qmin[qmin.length-1]]>line[j]) qmin.pop() qmin.push(j) while(qmax.length>0 && line[qmax[qmax.length-1]]<line[j]) qmax.pop() qmax.push(j) j++ } // j到最后一个了 while(qmin.length>0&&qmax.length>0&&line[qmax[0]]-line[qmin[0]]<=n){ res+=j-i if(qmin[0]==i)qmin.shift() if(qmax[0]==i)qmax.shift() i++ } console.log(res)
import java.util.*; import java.io.*; public class Main{ public static void main(String[] args){ Scanner sc=new Scanner(new BufferedInputStream(System.in)); int n=sc.nextInt(); int num=sc.nextInt(); int[] arr=new int[n]; for(int i=0;i<n;i++){ arr[i]=sc.nextInt(); } int[] min=new int[n]; int[] max=new int[n]; int minl=0; int minr=-1; int maxl=0; int maxr=-1; int i=0; int j=0; int res=0; while(i<n){ while(j<n){ while(minl<=minr&&arr[min[minr]]>=arr[j]) minr--; min[++minr]=j; while(maxl<=maxr&&arr[max[maxr]]<=arr[j]) maxr--; max[++maxr]=j; if(-arr[min[minl]]+arr[max[maxl]]>num){ break; } j++; } res+=j-i; if(max[maxl]==i) maxl++; if(min[minl]==i) minl++; i++; } System.out.println(res); } }
import java.util.*; public class Main{ public static void main(String[] args){ Scanner sc = new Scanner(System.in); int n = sc.nextInt(); int num = sc.nextInt(); int[] arr = new int[n]; for(int i = 0; i < n; i++) { arr[i] = sc.nextInt(); } int res = getArrayNum(arr, num); System.out.println(res); } public static int getArrayNum(int[] arr, int num) { if(arr == null || arr.length == 0) return 0; LinkedList<Integer> qmax = new LinkedList<>(); LinkedList<Integer> qmin = new LinkedList<>(); int res = 0; int L = 0; int R = 0; while(L < arr.length) { while(R < arr.length) { while(!qmax.isEmpty() && arr[qmax.getLast()] <= arr[R]) qmax.removeLast(); qmax.add(R); while(!qmin.isEmpty() && arr[qmin.getLast()] >= arr[R]) qmin.removeLast(); qmin.add(R); if(arr[qmax.getFirst()] - arr[qmin.getFirst()] > num) break; R++; } res += R - L; if(qmax.getFirst() == L) qmax.removeFirst(); if(qmin.getFirst() == L) qmin.removeFirst(); L++; } return res; } }
#include <bits/stdc++.h> using namespace std; int main(){ int n, num; cin>>n>>num; vector<int> v(n); for(int i=0;i<n;i++) cin>>v[i]; deque<int> dq_max; deque<int> dq_min; int i=0, j=0, res=0; while(i < n){ while(j < n){ if(dq_min.empty() || dq_min.back()!=j){ while(dq_min.empty()==false && v[dq_min.back()]>v[j]) dq_min.pop_back(); dq_min.push_back(j); while(dq_max.empty()==false && v[dq_max.back()]<v[j]) dq_max.pop_back(); dq_max.push_back(j); } if(v[dq_max.front()] - v[dq_min.front()] > num) break; ++j; } res += j-i; if(dq_max.front() == i) dq_max.pop_front(); if(dq_min.front() == i) dq_min.pop_front(); ++i; } cout<<res; return 0; }
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.LinkedList; public class Main { /** * 利用两个双端队列来实现子数组最大值和最小值的更新 */ public static int getNum(int[] arr, int num) { // 排除三种特例:null 空数组[] num小于0 if (arr == null || arr.length < 1 || num < 0) { return -1; } // 初始化子数组的最大值候选数组的下标 LinkedList<Integer> qmax = new LinkedList<>(); // 初始化子数组的最小值候选数字的下标 LinkedList<Integer> qmin = new LinkedList<>(); int res = 0; int i = 0; int j = 0; // 求以arr[i]开头的子数组中有多少个满足条件的 while (i < arr.length) { while (j < arr.length) { // 更新j++后的最大值和最小值 while (!qmax.isEmpty() && arr[qmax.peekLast()] <= arr[j]) { qmax.pollLast(); } qmax.addLast(j); while (!qmin.isEmpty() && arr[qmin.peekLast()] >= arr[j]) { qmin.pollLast(); } qmin.addLast(j); if (arr[qmax.peekFirst()] - arr[qmin.peekFirst()] > num) { // [i,j]不满足条件,则[i,j+1]...[i,arr.length-1]均不满足条件 break; } j++; } // [i,i]...[i,j-1]均满足条件,共有j-1-i+1个 res += j - i; // 更新i++后的最大值和最小值 if (qmax.peekFirst() == i) { qmax.pollFirst(); } if (qmin.peekFirst() == i) { qmin.pollFirst(); } i++; } return res; } public static void main(String[] args) throws IOException { BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in)); String[] numbers = bufferedReader.readLine().split(" "); int n = Integer.valueOf(numbers[0]); int num = Integer.valueOf(numbers[1]); int[] arr = new int[n]; numbers = bufferedReader.readLine().split(" "); for (int i = 0; i < n; i++) { arr[i] = Integer.valueOf(numbers[i]); } System.out.println(getNum(arr, num)); } }