达达现在碰到了一个棘手的问题,有 NN个整数需要排序。
达达手头能用的工具就是若干个双端队列。
她从 1 到 N 需要依次处理这 N 个数,对于每个数,达达能做以下两件事:
1.新建一个双端队列,并将当前数作为这个队列中的唯一的数;
2.将当前数放入已有的队列的头之前或者尾之后。
对所有的数处理完成之后,达达将这些队列按一定的顺序连接起来后就可以得到一个非降的序列。
请你求出最少需要多少个双端序列。
输入格式
第一行输入整数 N,代表整数的个数。
接下来 N 行,每行包括一个整数 Di,代表所需处理的整数。
输出格式
输出一个整数,代表最少需要的双端队列数。
数据范围
1≤N≤200000
输入样例:
6
3
6
0
9
6
3
输出样例:
2
思路:单谷性质:
直接模拟的话很难做,所以我们需要换一个思路:
对于样例中的元素:3,6,0,9,6,3;
将它排序后:0,3,3,6,6,9;
他们的下标(从1算):3,1,6,5,2,4,由两个双端队列组成;
我们可以看出3,1,6是先递减再递增,5,2,4也是先递减再递增,两个单谷对应两个双端队列;
所以结论:一个单谷对应一个双端队列;
单谷怎样转化为双端队列呢:递减的一段可以看作从队头插入,递增的一段可以看作队尾插入;
对于样例 单谷 3,1,6,一开始只有下标1的元素,然后将下标为3的元素插入队头,将下标6的元素插入队尾;
另外注意:对于数组中的几个值相同的元素,对于这几个数我们可以任意交换他们的顺序;
也就是可以把他们看作一个区间拼接在序列末尾,所以我们把排序后的下标数组分成若干区间;
用一个变量记录当前序列末尾递减还是递增的状态,贪心的把当前区间递增或递减的拼接在末尾;
对于样例:(res=1表示队列段数,当序列从递增状态变为递减时,res++)
1:第一个区间始终看作递减插入:序列为3,处于递减;
2:第二个区间1,6,由于6>3,无法递减,所以递增的插入,序列为3,1,6,递增;
3:第三个区间5,2由于5<6,无法递增,所以递减插入,res++,序列为3,1,6,5,2,递减;
4:第四个区间4,由于4>3所以无法递减,递增插入,序列为3,1,6,5,2,4,递减;
所以双端队列数为2;y总讲解
#include<iostream>
#include<algorithm>
#include<limits.h>
using namespace std;
const int N=2e5+10;
pair<int,int>a[N];//first表示值,second表示下标
int main()
{
int n;
cin>>n;
for(int i=0;i<n;++i) {
cin>>a[i].first;
a[i].second=i;
}
sort(a,a+n);//升序排序
int res=1,last=INT_MAX,dir=-1;//初始表示递减
for(int i=0;i<n;){//循环不用++,最后i=j
int j=i+1;//j表示第一个值不等于i的元素;
while(j<n&&a[j].first==a[i].first) j++;
int minx=a[i].second,maxx=a[j-1].second;
if(dir==-1){//如果递减
if(last>maxx) last=minx;//继续递减
else {
dir=1;//递减变递增
last=maxx;
}
}
else{
if(last<minx) last=maxx;//继续递增
else{
last=minx;//递增变递减
res++;//队列数+1
dir=-1;
}
}
i=j;//[i,j-1]的值已经用过,更新i的值
}
cout<<res;
return 0;
}