《算法竞赛进阶指南》 双端队列

达达现在碰到了一个棘手的问题,有 NN 个整数需要排序。

达达手头能用的工具就是若干个双端队列。

她从 11 到 NN 需要依次处理这 NN 个数,对于每个数,达达能做以下两件事:

1.新建一个双端队列,并将当前数作为这个队列中的唯一的数;

2.将当前数放入已有的队列的头之前或者尾之后。

对所有的数处理完成之后,达达将这些队列按一定的顺序连接起来后就可以得到一个非降的序列。

请你求出最少需要多少个双端序列。

输入格式

第一行输入整数 NN,代表整数的个数。

接下来 NN 行,每行包括一个整数 DiDi,代表所需处理的整数。

输出格式

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

数据范围

1≤N≤2000001≤N≤200000

输入样例:

6
3
6
0
9
6
3

输出样例:

2

 此题解借鉴于acwing的大佬:AcWing 134. 双端队列 - AcWing

 

解题代码

 

/*
 * Project: 0x12_Queue
 * File Created:Friday, January 1st 2021, 13:31:39 pm
 * Author: Bug-Free
 * Problem:AcWing 134. 双端队列
 */
#include <algorithm>
#include <iostream>

using namespace std;

const int N = 2e5 + 10;

typedef pair<int, int> PII;

#define value first
#define before_sort second

PII a[N];
int n;

int main() {
    cin >> n;
    for (int i = 0; i < n; i++) {
        cin >> a[i].value;
        a[i].before_sort = i;
    }
    sort(a, a + n);
    int res = 1;  // 最后最少分成的段数
    // last记录一下上一个值(上一段的最后一个值是多少),
    // 初始化为比所有坐标都大即可(>n的数字均可) dir用来记录方向
    // 第一条边一定是下降的, 因此dir初始化为-1(可以理解为是从上面接下来的)
    for (int i = 0, last = n + 1, dir = -1; i < n;) {
        int j = i + 1;
        // 找到一段相等的数值
        // 如果这一段的数字是相等的, 那么这些下标是可以随意变换的
        // 从i-j-1这一段, 都是和a[i].value相同的数字
        while (j < n && a[j].value == a[i].value) {
            j++;
        }
        // 找到这一段中的最大的下标和最小的下标
        // 因为是sort(pair经过sort)过的, 因此minx一定这一段的第一个数的位置
        // maxx一定是这一段的最后一个数的位置
        // 在原序列中的的位置
        // minx=a[i].before_sort, maax = a[j-1].before_sort;
        // 我们处理的时候是一个点一个点进行处理的(如果有相同的, 则处理一段,
        // 因为相同的可以任意插入到一个双端队列)
        int minx = a[i].before_sort, maxx = a[j - 1].before_sort;
        if (dir == -1) {        //如果是下降的, 如何做?
            if (last > maxx) {  //如果上一个位置>当前的最大位置,
                                //那么就可以接到上一个位置的后面
                last =
                    minx;  // 接上了的话, 这一段的最后一个数值(原坐标)应该是minx
            } else {  //如果没有接上, 那么更新方向为上升, last=maxx
                      // 此时不需要res++的原因是 谷是先下降, 后上升的
                      // 上升结束之后, 才需要开启新的谷
                dir = 1, last = maxx;  // 上升的时候last更新为最大值,
            }
        } else {  // 如果是上升的, 如何做?
            if (last <
                minx) {  // 如果last比我当前这一段的最小值还要小, 那么可以接上去
                last = maxx;
            } else {  // 否则开新双端队列+更新last为这一段的最小值,
                      // 更新方向为下降
                res++;
                last = minx;
                dir = -1;
            }
        }
        i = j;
    }
    cout << res << endl;
    return 0;
}

 作者:个人空间 - AcWing

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

啥也不会hh

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值