https://www.acwing.com/problem/content/136/
题意
Sherry要通过双端队列对N个数进行排序操作。对于每个数 A [ i ] A[i] A[i],只允许进行以下操作。
- 新建一个双端队列并将 A [ i ] A[i] A[i]入队
- 把 A [ i ] A[i] A[i]从头或尾入队至已有的队列之中。
此外,所有的数处理完成之后,这些队列必须能够被排成非降序列。
求至少需要多少个双端队列
思路
所有的数处理完成之后,这些队列必须能够被排成非降序列。这一要求给实现带来了障碍。因为如果按照读入顺序,未读入的数是不可知的,而题目并不允许出队操作。这也就意味着如果两个数P和Q被放在同一个队列中,而之后又出现了介于P和Q之间的数,那么不论如何操作都不能够满足“所有的数处理完成之后,这些队列必须能够被排成非降序列”这一要求(因不论如何拼接队列都会出现逆序情况)。
这种情况 启发我们不能按照读入顺序,而应该按照数值大小顺序进行计算。
虽然题目描述的是一种以队列实现排序的方式,但我们反向考虑,考虑对于排序好的N个数至少需多少个双端队列与之对应。
注意到题意中,允许将 A [ i ] A[i] A[i]从队头或队尾入队。那么我们不妨思考,这一操作对应着怎样的性质呢?
我们考虑这样一个样例
3
,
6
,
0
,
9
,
6
,
3
3,6,0,9,6,3
3,6,0,9,6,3,下标分别是
1
,
2
,
3
,
4
,
5
,
6
1,2,3,4,5,6
1,2,3,4,5,6
将其升序排序我们得到
0
,
3
,
3
,
6
,
6
,
9
0,3,3,6,6,9
0,3,3,6,6,9,下标分别是
3
,
1
,
6
,
2
,
5
,
4
3,1,6,2,5,4
3,1,6,2,5,4
观察到,对于排序后的下标数组中每个“单谷”,即先减后增的子段,都可以对应一个单独的双端队列。
我们仍然以刚才的样例进行说明。
3
,
1
,
6
,
2
,
5
,
4
3,1,6,2,5,4
3,1,6,2,5,4 不难看出
3
,
1
,
6
3,1,6
3,1,6 就是这样一个“单谷”序列。
那么首先我们把谷底,即下标1对应的元素(3)入队
[
3
]
[3]
[3]
接下来,我们把递减子段下标3对应的元素(0)头插入
[
0
,
3
]
[0,3]
[0,3]
最后我们把递增子段下标6对应的元素(3)尾插入
[
0
,
3
,
3
]
[0,3,3]
[0,3,3]
我们可以看到,队列中的元素已经被排序了。
那么,为什么是“单谷”性质呢?
其实,这就是说原数组中存在“逆序”的部分。
我们仍然以刚才的
3
,
1
,
6
3,1,6
3,1,6为例。此时,我们知道
A
[
3
]
≤
A
[
1
]
≤
A
[
6
]
A[3]\leq A[1] \leq A[6]
A[3]≤A[1]≤A[6]
即
0
≤
3
≤
3
0 \leq 3 \leq 3
0≤3≤3
可以看出,原数组中下标
1
,
3
,
6
1,3,6
1,3,6对应的元素
3
,
0
,
3
3,0,3
3,0,3是逆序的,因此我们才需要通过双端队列的头插入和尾插入,将其调整到正确的位置。
如果不满足“单谷”条件,则对应以下几种情况
- 排序序列的下标递增,即升序情况。
- 排序序列的下标递减,即降序情况。
有同学可能会考虑,是否有“单峰”的情况呢?然而事实上,将“单谷”的情况处理完之后,“单峰”的情况也就不复存在了。
另外,如果存在相同的元素,那么我们可以任意交换它们的顺序以达成更小的段数。
#include <iostream>
#include <algorithm>
using namespace std;
typedef pair<int, int> PII;
const int N = 200020;
PII a[N];
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); // pair是双关键字排序
int res = 1; // 初始时有一个双端队列
for (int i = 0, last = 2e9, direction = -1; i < n;)
{
int j = i;
while (j < n && a[i].first == a[j].first)
j++;
int maxx = a[j - 1].second;
int minx = a[i].second;
if (direction == -1)
{
if (last > maxx)
last = minx; // 当前下降序列,可被插入双端队列尾部
else
{
last = maxx;
direction = 1;
}
}
else
{
if(last>=minx){
res++;
direction=-1;
last=minx;
}else{
last=maxx;
}
}
i=j;
}
cout<<res<<endl;
return 0;
}