P3512 [POI2010]PIL-Pilots 单调队列的应用

题目描述

给定n,k和一个长度为n的序列,求最长的最大值最小值相差不超过k的序列

输入格式

第一行两个有空格隔开的整数k(0<=k<=2000,000,000),n(1<=n<=3000,000),k代表设定的最大值,n代表序列的长度。第二行为n个由空格隔开的整数ai(1<=ai<=2000,000,000),表示序列。

输出格式

一个整数代表最大的符合条件的序列

首先解释一下题目:这道题让我们求出一个长度为n的序列中最长的一段子序列,满足这段子序列的最大值减最小值的差不超过k,求子序列的最大长度。
因为单调队列可以记录下一段区间的最大值和最小值,所以这道题我们完全可以用一个单调队列来实现。

我们设立一个l和r变量,分别表示子区间的左边界和右边界。接下来我们对任意一个子序列进行移动:
移动操作有两种情况,第一种即当该单调区间的最大值减去最小值的差小于等于k时,该单调区间是一个合法的区间。这时我们可以对答案进行记录,然后让r++,再进行判断是否会有更大的区间。

第二种情况,即当该单调区间的最大值减去最小值的差大于k时,该区间是一个不合法的区间。这时候我们不能将r++,因为这种操作不可能减小该区间内的最大值或增大该区间内的最小值,使变成一个合法的区间。我们只能将l++,然后注意要根据之前的a[l]和maxn,minn之间关系的三种情况对最大值和最小值进行维护。注意,单调队列是单调的,它只能往右移动,绝对不能出现往左移动的状况,即绝对不能l--或r--

这样我们就成功维护了单调队列的操作。

代码:

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int inf=0x3f3f3f3f;
 4 int k,n,l,r,maxn,minn,ans;
 5 int a[3000005];
 6 void find(int l,int r){
 7     maxn=-inf;minn=inf;
 8     for(int i=l;i<=r;i++){
 9         maxn=max(maxn,a[i]);
10         minn=min(minn,a[i]);
11     }
12 }
13 int main(){
14     cin>>k>>n;
15     for(int i=1;i<=n;i++){
16         cin>>a[i];
17     }
18     l=1;r=2;
19     maxn=max(a[1],a[2]);
20     minn=min(a[1],a[2]);
21     while(1){
22         if(maxn-minn>k){
23             if((a[l]==minn)||(a[l]==maxn)){
24                 l++;
25                 find(l,r);
26             }else if(a[l]>minn){
27                 l++;
28             }
29         }else{
30             r++;
31             if(a[r]<minn){
32                 minn=a[r];
33             }else if(a[r]>maxn){
34                 maxn=a[r];
35             }
36             if(maxn-minn<=k){
37                 ans=max(ans,r-l+1);
38             }
39         }
40         if(r==n){
41             break;
42         }
43     }
44     cout<<ans<<endl;
45     return 0;
46 }
原文地址:https://www.cnblogs.com/qianr/p/13832292.html