第十二届蓝桥杯B组省赛双向排序题解

请添加图片描述
请添加图片描述

题目分析

以123456789为例
如果我们将第4位之前的元素降序排列
432156789
然后对任何位置后的元素升序排列都不会影响到最后的56789
如果接下来的若干次操作都没有使得>=4的位置n前面的元素降序排列,那么对n之前的元素降序排列将会撤销之前的所有操作,设n=6我们会得到
654321789
同理设i>=m,对小于m的元素降序排列将撤回许多操作,直到遇到操作"对i前的元素降序排列"。
还可以发现对于较大的n操作"将n前的元素降序排列"将会吸收若干连读的"将m前的元素降序排列"操作,其中m<=n
将m右侧的元素升序排列也有类似的性质
最终剩下的操作一定是交替出现的,n严格递减,m严格递曾的“对n前的元素降序排列”操作与“对m后的元素升序排列”操作
对于这类操作,每次将操作共同影响的区间反向就行了。
比如n,m依次为(操作时包括第m,n个元素)
9 1
8 2
7 3
对123456789是
9 1
987654321
123456789
8 2
876543219
812345679
7 3
865432179
861234579

算法实现

细节较多,讲不清楚,建议在纸上自己画画
代码中有详细注释

#include<bits/stdc++.h>
#define NMAX 100001 //最后多一个元素用来取地址给reverse用
//n:new
#define nd 0 //新元素类型为左递减
#define nu 1 //新元素类型为右递增
//d:down,u:up
int d[NMAX+1]={NMAX};//左递减操作序列,用大值托底(但这不算一次操作)
int u[NMAX+1]={0,1};//右递增操作序列,用小值托底(这个算一次操作)
//sd:sizeofDown
int sd=0,su=1;//左递减操作序列、右递增操作序列的大小,认为右递增序列中有一个元素1,因为序列最初是从1开始右递增的,可认为是第一次操作的结果
int N,T;//数字序列长度,操作次数
int A[NMAX+1];//保存数字序列
using namespace std;
int main(){
    cin >> N >> T;
    //v:value
    int type,v;
    while(T--){
        cin >> type >> v;
        if(type==nd){//如果是左递减操作
            if(v>=d[sd]){//撤回无用操作
                while(v>=d[sd])sd--;
                ++sd,su=sd,d[sd]=v;
            }else if(sd<su){//不能撤回就添加新操作(连续较小值的同种操作被sd<su吸收了)
                ++sd,d[sd]=v;
            }
        }else{
            if(v<=u[su]){//同上
                while(v<=u[su])su--;
                ++su,sd=su-1,u[su]=v;
            }else if(sd==su){
                ++su,u[su]=v;
            }
        }
    }
    for(int i=1;i<=N;++i){//初始值
        A[i]=i;
    }
    //p:pointer,u:up,d:down
    int pu=1;//现在从右递减的第一步开始执行操作
    int pd=1;
    while(pu<su){
        if(u[pu]<d[pd])//之前没考虑过m,n相撞的情况,在这里要避免
            reverse(&A[u[pu]],&A[d[pd]+1]);
        ++pu;
        if(u[pu]<d[pd])
            reverse(&A[u[pu]],&A[d[pd]+1]);
        ++pd;
    }
    if(su==sd){//如果两个大小一样则还差一步操作
        if(u[pu]<d[pd])
            reverse(&A[u[pu]],&A[d[pd]+1]);
    }
    for(int i=1;i<=N;++i){
        cout << A[i] << ' ';
    }
    cout << endl;
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值