【ACWing】134. 双端队列

题目地址:

https://www.acwing.com/problem/content/136/

达达现在碰到了一个棘手的问题,有 N N N个整数需要排序。达达手头能用的工具就是若干个双端队列。她从 1 1 1 N N N需要依次处理这 N N N个数,对于每个数,达达能做以下两件事:
1 1 1.新建一个双端队列,并将当前数作为这个队列中的唯一的数;
2 2 2.将当前数放入已有的队列的头之前或者尾之后。
对所有的数处理完成之后,达达将这些队列按一定的顺序连接起来后就可以得到一个非降的序列。请你求出最少需要多少个双端序列。

输入格式:
第一行输入整数 N N N,代表整数的个数。
接下来 N N N行,每行包括一个整数 D i D_i Di,代表所需处理的整数。

输出格式:
输出一个整数,代表最少需要的双端队列数。

数据范围:
1 ≤ N ≤ 200000 1≤N≤200000 1N200000

由于将所有队列连接起来之后就可以得到一个非降的序列,所以插入完毕之后,每个队列都是非降的。设数组为 a a a,不妨设 a 1 , a 2 , . . . , a k a_1,a_2,...,a_k a1,a2,...,ak是放在同一个双端队列里的,那么很显然,对于 a i a_i ai而言,其必然或者小于等于 a [ : i − 1 ] a[:i-1] a[:i1]的最小值,或者大于等于 a [ : i − 1 ] a[:i-1] a[:i1]的最大值,所以如果对 a [ 1 : k ] a[1:k] a[1:k]排序,那么其下标一定是形如 b 1 ≥ b 2 ≥ . . . ≥ b s ≤ b s + 1 ≤ . . . b_1\ge b_2\ge ...\ge b_s\le b_{s+1}\le ... b1b2...bsbs+1...这样的。所以,如果将整个数组 a a a排序,一定能划分为若干个子数组,使得每个子数组对应一个队列,并且该子数组内部的下标满足先降后升,即 V V V字形。那么问题转化为,要将一个数组划分为若干先降后升的子数组,最少能划分为多少个子数组,可以用贪心的办法,从第一个数开始先向下走,如果走不动了,再上升,再走到走不动的时候,就完成了一个周期,可以继续向下,这样重复操作(注意,这里需要考虑一种特殊情形,即有相同元素的情形,在这种情形之下,有可能这些相同元素按上面的做法被分到了多个队列里,而最优的方法显然是将它们放到相同队列里。总可以适当调整顺序使得它们都被放到同一个队列里)。贪心的正确性是不难证明的,如果不像上面这样做,则可以做若干次合并使得结果与上面相同。

#include 
#include 
#include 
#define x first
#define y second
using namespace std;
using PII = pair<int, int>;

const int N = 2e5 + 10;
int n;
PII a[N];

int main() {
  scanf("%d", &n);
  for (int i = 1; i <= n; i++) {
    scanf("%d", &a[i].x);
    // 第二位存下标
    a[i].y = i;
  }

  // 按数值排序,如果数值相同,则按下标排序
  sort(a + 1, a + 1 + n);
  // dir为-1表示下降,1表示上升,last表示上个数的下标,
  // 由于一开始要下降,所以设为正无穷
  int res = 1, last = INT_MAX, dir = -1;
  for (int i = 1; i <= n;) {
    int j = i;
    while (j <= n && a[j].x == a[i].x) j++;
    // 找到值相等的数中,最小下标和最大下标
    int minp = a[i].y, maxp = a[j - 1].y;
    if (dir == -1) {
      // 如果是下降趋势,并且上个数下标大于最大下标,
      // 则这段数可以按下标下降的顺序接到后面,否则
      // 要改变方向,将这段数按照下标上升的顺序接到后面
      if (last > maxp) last = minp;
      else dir = 1, last = maxp;
    } else {
   	  // 同上。如果last > minp,说明这一段数可以按下标下降
   	  // 的顺序接到后面,并且这段数开启了一个新周期,所以res加1
      if (last < minp) last = maxp;
      else {
        res++;
        dir = -1;
        last = minp;
      }
    }

    i = j;
  }

  printf("%d\n", res);
}

时间复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn),空间 O ( n ) O(n) O(n)

你可能感兴趣的:(AC,贪心与动态规划,贪心算法,算法,c++)