noip模拟赛 可耻

题目描述

给定一个长度为偶数的排列 p,你每次可以选取 p 排列中相邻的两个元素,假如分别是 x 和 y,那 么把 x 和 y 加入一个序列 q 的末尾,并将 x 和 y 从排列 p 中删除。重复上述过程,直到 p 中没有元素, 显然最终 q 序列也是一个排列。例如 p = (1, 3, 2, 4),第一次可以选取 3, 2 这两个元素加入 q,并且从 p 中删除,此时 p = (1, 4),第二次可以选取 1, 4 这两个元素加入 q,此时 p 为空,q = (3, 2, 1, 4)。观察上 述过程,最终 q 序列可能会有很多方案,请你输出所有可能方案中,q 的字典序最大的。

字典序的比较方式如下,假设有两个长度同为 n 的序列 a, b。我们找到最大的 t,使得对于 ∀i ≤ t, ai = bi。之后比较 a[t+1] 与 b[t+1],如果 a[t+1] < b[t+1],我们就认为 a 的字典序比 b 小,反之亦然。

输入输出格式

输入格式:

第一行包含一个正整数 n。第二行 n 个数,表示排列 p。

输出格式:

一行 n 个数,表示字典序最大的序列 q。

输入输出样例

输入样例#1:
4 
3 1 4 2
输出样例#1: 
4 2 3 1
输入样例#2: 
6 
6 5 4 1 3 2
输出样例#2: 
6 5 4 1 3 2

说明

测试点编号  限制与约束
1,2 n<=10n<=10
3,4,5,6 n<=10^3n<=10^3
7,8,9,10 n<=10^5n<=10^5

 

分析:因为P是一个排列,又要使得字典序最大,所以每次肯定要删掉最大的没有删掉的数和它后面的一个没有被删掉的数.如果只用数组来操作,每次删除操作后都要移动大量的元素,所以用上链表,再用一个vis数组记录每个数有没有被删,从n到1扫一遍就可以了.

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

int n, p[100010], l[100010], r[100010];
bool vis[100010];

void shan(int x)
{
    vis[x] = 1;
    vis[r[x]] = 1;
    printf("%d %d ", x, r[x]);
    r[l[x]] = r[r[x]];
    l[r[r[x]]] = l[x];
}

int main()
{
    scanf("%d", &n);
    for (int i = 1; i <= n; i++)
        scanf("%d", &p[i]);
    for (int i = 1; i <= n; i++)
    {
        l[p[i]] = p[i - 1];
        r[p[i]] = p[i + 1];
    }
    for (int i = n; i >= 1; i--)
    {
        if (!vis[i] && r[i] != 0)
            shan(i);
    }

    return 0;
}
原文地址:https://www.cnblogs.com/zbtrs/p/7709636.html