双端队列

# 题意
n个数进行排序,只能用双端队列,只能进行两个操作
1、新建双端队列,将当前书作为队列中的唯一数
2、将当前数放入已有的队列之前或尾后
最后所有队列按一定顺序连接起来的答案求出最小的队列数

# 题解
直接模拟求解很难,局部决策可能会导致后面有一个数大小位于已经插入的两个数中间,因为只能使用双端队列,所以错误

反向思考问题,把一个A[ 1 , n ]的已经排好顺序的序列,分成尽量少的段,
每一段都是一个双端队列.

单谷性质(先单减,后递增)
开一个数组也就是B数组,记录原数组A的每一个数的下标,然后我们发现如果B的一段满足单单谷性质,那么这一段就可以形成合法的双端序列.
因为单谷性质,前半段可以视为从队头加入,后半段视为从队尾加入.
数值相同的点的下标可以交换用来满足单谷序列
所有值相同的都是一个vector,对于每一个值的下标都从小到大排序
贪心过程:
求单调递减时候,如果上一个点的下标比当前值下标最大的还要大,那么显然将下标最小的点放到最后可以将所有当前值都存入,
否则就证明到达谷底,结束的即为当前最大值,因为接下来就要检查递增,而递增是优先放下标最大的所以如果转到求递增的时候,下标是连续的相同值的下标,显然最优
求递增的时候如果值相同,上一个点的下标比当前的值下标最小的还要小就一定可以一直放,直到最大
贪心求得最少的谷底数,即为最少的单谷段,即答案
时间复杂度O(nlogn)

 1 #include<bits/stdc++.h>
 2 #define pii pair<int,int>
 3 #define pb push_back
 4 #define fi first
 5 #define se second
 6 using namespace std;
 7 const int N=2e5+10;
 8 pii a[N];
 9 vector<int>p[N];
10 int main(){
11    int n;
12    cin>>n;
13    for(int i=0;i<n;i++){
14       cin>>a[i].fi;
15       a[i].se=i;
16    }
17    sort(a,a+n);
18    int tot=0;
19    for(int i=0;i<n;i++){
20       p[++tot].pb(a[i].se);
21       while(a[i+1].fi == a[i].fi)
22          p[tot].pb(a[++i].se);
23    }
24    for(int i=1;i<=tot;i++)
25       sort(p[i].begin(),p[i].end());
26 
27    bool flag=0;//记录是否到达转折点
28    int pos=INT_MAX,ans = 1;
29    for(int i=1;i<=tot;i++){
30       int ed=p[i].size()-1;
31       if(flag){//求递增
32          if(pos < p[i][0])
33             pos = p[i][ed];//无法递增移动端点,区间个数+1
34          else{
35             ++ans;
36             pos = p[i][0];
37             flag=0;
38          }
39       }
40       else {//求递减
41          if(pos > p[i][ed])
42             pos = p[i][0];
43          else{
44             flag = 1;
45             pos = p[i][ed];
46          }
47       }
48    }
49    cout<<ans<<endl;
50 }
原文地址:https://www.cnblogs.com/hhyx/p/12495719.html