NKOI半期 2513 切序列

Problem 2

切序列(cut.cpp/c/pas)

题目描述

何老板发现了一个有趣的数列问题,他想用它来考考你。

给你一个由n个非负整数构成的数列{A1, A2, ..., An} 其中有

 A1 > A2, ..., An, 也就是第一项是数列中最大的一个数。

 你要做的是把这个数列切割成3,切割后把每一段的数字都前后颠倒,然后再把这三段都连接起来,得到一个新的数列,要求这个新的数列的字典序最小。详情见样例。

输入格式

第一行,一个正整数N

第二行,N个空格间隔的整数,表示给出的数列。

输出格式

一行,N个空格间隔的整数,表示所求的数列。

输入样例

输入样例1

输入样例2

5

10 1 2 3 4

10 0 2 1 5 2 3

输出样例

输出样例1

输出样例2

1 10 2 4 3

0 10 1 2 3 2 5

数据范围

对于50%的数据:  1N100

对于100%的数据: 1N200000

数列中的数字≤10000

样例说明

样例1说明:

{10,1,2,3,4} -> {10,1|2|3,4} -> {1,10|2|4,3}-> {1,10,2,4,3}

样例2说明:

{10,0,2,1,5,2,3}->{10,0|2,1|5,2,3}->{0,10|1,2|3,2,5}->{0,10,1,2,3,2,5}

方法:最小表示法
1.将输入的数组a反序存储到数组s
2.在数组s上求最小表示,得到结果x,则切割出的第一段为a数组中[x,n]这段区间 ; 
3.在数组s的[1,x-1]区间求最小表示,得到结果y,则切割出的第二段为a数组的[y,x-1]这段区间;
4.显然,第三段为区间[1,y-1]
注意,为保证一定能切割出三段,所以第一次求最小表示的起点为3,第二次求最小表示的起点为2。
比如下列特例:
5
5 4 3 2 1
按常规来求的话,第一次最小表示求出的值为1,切割出的区间为[1,5],这样就无法在再切割其余两段了。 
#include<iostream>
#include<cstdio>
#include<cstdlib>
using namespace std;
int a[200005],s[400005],n;
int work(int Len,int st){
    int k,i=st,j=st+1;
    while(i<=Len&&j<=Len){
        for(k=0;k<Len;k++)
            if(s[i+k]!=s[j+k])break;
        if(k==Len)break;
        if(s[i+k]>s[j+k])i+=k+1;
        else if(s[i+k]<s[j+k])j+=k+1;
        if(i==j)j++;
    }
    return min(i,j);
}//最小表示法
int main(){
    int n,i,x,y;
    scanf("%d",&n);
    for(i=n;i>=1;i--)scanf("%d",&a[i]);
        for(i=1;i<=n;i++){
            s[i]=a[i];
            s[i+n]=a[i];
        }
    x=work(n,3);
    for(i=x;i<=n;i++)printf("%d\n",a[i]);
    for(i=1;i<x;i++){
        s[i]=a[i];
        s[i+x-1]=a[i];
    }
    y=work(x-1,2);
    for(i=y;i<x;i++)printf("%d\n",a[i]);
    for(i=1;i<y;i++)printf("%d\n",a[i]);
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值