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);
}//最小表示法
void out(int x,int y){
for(int i=x;i<=y;i++)printf("%d\n",a[i]);
}//输出函数
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);
out(x,n);
for(i=1;i<x;i++){
   s[i]=a[i];
s[i+x-1]=a[i];
}
y=work(x-1,2);
out(y,x-1);
out(1,y-1);
}

你可能感兴趣的:(NKOI半期 2513 切序列)