方法一:暴力搜索。
比较容易想到,先按照全排列的方法,找出火车进站序列的全排列。从全排列序列中找到符合出站规则的序列,再将所有的合法序列排序,最后依次打印输出。
出站规则:用栈储存入站火车序号,另一边用一个指针顺序遍历当前的全排列序列,对比当前栈顶序号是否与指针所指的全排列序列号相等。
相等就指针右移,栈顶序号出栈。若栈空就进栈下一个火车序号,继续与指针所指序号对比。
时间复杂度:O(n*n!),空间复杂度O(catalan(n)),排序时用到了卡特兰数大小的空间。
/*从全排列序列中找出正确的出栈序列*/
#include <stdio.h>

int p=0;
void legal(int* a,int* b,int len,int c[][len],long d[][2]){//判断全排列的其中一个序列是否合法,并将值存入out
    int in[len],top=0,i=1,j=0,flag=0;//top为入站序列的栈顶指针,i为入站序列指针,j为出站序列指针
    in[top]=a[0];
    while(j<len){//如果还有火车没有出站
        if(in[top]==b[j]){
            top--;
            if(top==-1 && i<len){//车站空就入站
                in[++top]=a[i++];
            }
            j++;
        }
        else if(i<len){
            in[++top]=a[i++];
        }
        else{
            flag=1;
            break;
        }
    }
    if(!flag){//合法就输出该序列
        long sum=0;
        for(int j=0;j<len;j++){
            c[p][j]=b[j];
            sum=10*sum+b[j];
        }
        d[p][0]=sum;
        d[p][1]=p;
        p++;
    }
}
void sort(int* a,int* b,int start,int len,int c[][len],long d[][2]){//全排列出站序列,a[]为原入站序列,b[]为全排列序列
    int t;
    if(start>=len){
        legal(a,b,len,c,d);
    }
    for(int i=start;i<len;i++){
        if(start!=len){//交换两值后,对剩余len-i个元素全排列
            t=b[start];
            b[start]=b[i];
            b[i]=t;
        }
        sort(a,b,start+1,len,c,d);
        if(start!=len){//回溯,返回交换之前的状态
            t=b[start];
            b[start]=b[i];
            b[i]=t;
        }
    }
}
int catalan(n){//计算卡特兰数
    long x1=1,x2=1;
    for(int i=n;i>1;i--){
        x1*=i;
    }
    for(int i=2*n;i>n;i--){
        x2*=i;
    }
    x2/=x1*(n+1);
    return x2;
}
void bubble_sort(long a[][2],int n){
    for(int i=0;i<n-1;i++){
        long t1,t2,flag=0;
        for(int j=n-1;j>i;j--){
            if(a[j-1][0]>a[j][0]){
                t1=a[j][0];t2=a[j][1];
                a[j][0]=a[j-1][0];
                a[j][1]=a[j-1][1];
                a[j-1][0]=t1;
                a[j-1][1]=t2;
                flag=1;
            }
        }
        if(!flag){
            return;
        }
    }
}
int main(){
    int N;scanf("%d",&N);
    int a[11]={0},stack[N],in[N],I=0,t;
    int NN=catalan(N);
    int out[NN][N];long out1[NN][2];
    while(scanf("%d",&t)!=EOF){
        a[t]=1;
        stack[I++]=t;//stack[]用于储存入站火车序号
    }
    t=0;
    for(int i=1;i<11;i++){//为了优化最后排序的次数,in[]按序号大小存储入站火车号
        if(a[i]){
            in[t++]=i;
        }
    }
    sort(stack,in,0,N,out,out1);
    bubble_sort(out1,NN);
    for(int i=0;i<NN;i++){
        for(int j=0;j<N;j++){
            printf("%d ",out[out1[i][1]][j]);
        }
        printf("\n");
    }
}
执行耗时23ms,消耗内存560KB

优化:用按字典序的全排列,边排边输出,不用再排序。(c++可以用next_permutation()函数实现去重递增的字典排序)
时间复杂度:O(n*n!),空间复杂度O(n),最优的空间利用。
/*从全排列序列中找出正确的出栈序列*/
#include<stdio.h>  

void legal(int* a,int* b,int len){//判断全排列的其中一个序列是否合法
    int in[len],top=0,i=1,j=0,flag=0;//top为入站序列的栈顶指针,i为入站序列指针,j为出站序列指针
    in[top]=a[0];
    while(j<len){//如果还有火车没有出站
        if(in[top]==b[j]){
            top--;
            if(top==-1 && i<len){//车站空就入站
                in[++top]=a[i++];
            }
            j++;
        }
        else if(i<len){
            in[++top]=a[i++];
        }
        else{
            flag=1;
            break;
        }
    }
    if(!flag){//合法就输出该序列
        for(int j=0;j<len;j++){
            printf("%d ",b[j]);
        }
        printf("\n");
    }
}
int search(int* a,int key,int low,int high){//二分查找
    int mid;
    while(low<=high){
        mid=(low+high)/2;
        if(a[mid]==key){
            return mid;
        }
        else if(a[mid]<key){
            high=mid-1;
        }
        else{
            low=mid+1;
        }
    }
    if(a[mid]>key){
        return mid;
    }
    else{
        return mid-1;
    }
}    
void swap(int* a,int* b){//交换
    int c;
    c=*a;*a=*b;*b=c;
}     
void sort(int* a,int n,int* b){//改变当前序列为全排列按字典输出的下一个序列
    legal(b,a,n);
    int pos=n-1;
    while(pos>0 && a[pos]<a[pos-1]){
        pos--;
    }
    if(!pos){//已经完全逆序了就中止退出
       return; 
    }
    else{
        int p=search(a,a[pos-1],pos,n-1);//二分查找确定交换位置
        swap(&a[pos-1],&a[p]);
    }
    for(int i=pos;i<=(n-1+pos)/2;i++){//逆序pos后面的序列值
        swap(&a[i],&a[n-1+pos-i]);
    }
    sort(a,n,b);//继续排下一个
}
int main()
{
    int N;scanf("%d",&N);
    int a[11]={0},stack[N],in[N],I=0,t;
    while(scanf("%d",&t)!=EOF){
        a[t]=1;
        stack[I++]=t;//stack[]用于储存入站火车序号
    }
    t=0;
    for(int i=1;i<11;i++){//为了优化最后排序的次数,in[]按序号大小存储入站火车号
        if(a[i]){
            in[t++]=i;
        }
    }
    sort(in,N,stack);
}
执行耗时18ms,消耗内存328KB

方法二:利用DFS遍历所有的合法情况,最后排序输出。(可以使用C库函数stdlib自带的qsort()排序)
因为是基于DFS,它是将一种情况或条件递归到尽头的办法。所以可以把问题对象转移为车站里是否有火车,可以分出3种情况:
1.如果站内还有火车,火车立刻出站,递归到站内没有火车,回溯;
2.如果还有火车没有进站,火车立刻进站,递归到所有火车都进站,回溯;
3.如果所有火车都曾进站,且站内没火车,此次递归结束,输出一个序列;
时间复杂度O(n!),空间复杂度O(catalan(n)),排序时用到了卡特兰数大小的空间。
/*DFS迭代找出合法序列*/
#include <stdio.h>
#include <string.h>

int out_p=0;//out[]的指针,表明有多少火车已出站
int in_p=0;//in[]的指针,指向下一个入站火车
int stack_p=-1;//stack[]的指针,指向车站内的栈顶火车
int I=0;//记录合法序列个数
void dfs(int* a,int* b,int* c,int n,char d[][n]){
    if(out_p==n){//递归出口:当火车全部出站,记录该合法序列
        for(int i=0;i<n;i++){
            d[I][i]=a[i]+'0';
        }
        I++;
        return;
    }
    if(stack_p>-1){//递归场景1:如果站内还有火车,立刻出站,并使出站序列out_p++
        a[out_p++]=b[stack_p--];//火车出站
        dfs(a,b,c,n,d);
        b[++stack_p]=a[--out_p];//回溯
    }
    if(in_p<n){//递归场景2:如果还有火车没有进站,立刻进站,并使stack_p++
        b[++stack_p]=c[in_p++];//火车进站
        dfs(a,b,c,n,d);
        in_p--;stack_p--;//回溯
    }
}
int partition(int n,char a[][n],int low,int high){//快排中确定基准序号
    char* p=(char*)malloc(n);
    for(int i=0;i<n;i++){//原字符串没有'\0'结束符,无法使用strcpy()函数
        p[i]=a[low][i];
    }
    while(low<high){
        while(low<high && strcmp(a[high],p)>=0){
            high--;
        }
        for(int i=0;i<n;i++){
            a[low][i]=a[high][i];
        }
        while(low<high && strcmp(a[low],p)<=0){
            low++;
        }
        for(int i=0;i<n;i++){
            a[high][i]=a[low][i];
        }
    }
    for(int i=0;i<n;i++){
        a[low][i]=p[i];
    }
    free(p);
    return low;
}
void quicksort(int n,char a[][n],int low,int high){//快速排序
    if(low<high){
        int p=partition(n,a,low,high);
        quicksort(n,a,low,p-1);
        quicksort(n,a,p+1,high);
    }
}
int catalan(n){//计算卡特兰数
    long x1=1,x2=1;
    for(int i=n;i>1;i--){
        x1*=i;
    }
    for(int i=2*n;i>n;i--){
        x2*=i;
    }
    x2/=x1*(n+1);
    return x2;
}
int main(){
    int N;scanf("%d",&N);
    int NN=catalan(N);//计算有多少个合法序列
    char legal[NN][N];//为所有的合法序列预先分配空间
    int in[N],t,k=0;
    while(scanf("%d",&t)!=EOF){
        in[k++]=t;//in[]用于储存入站火车序号,即初始的序列
    }
    int out[N];//记录出站的火车序号
    int stack[N];//记录正在站内等候的火车序号
    dfs(out,stack,in,N,legal);//找出所有合法序列
    quicksort(N,legal,0,NN-1);//排序
    for(int i=0;i<NN;i++){
        for(int j=0;j<N;j++){
            printf("%c ",legal[i][j]);
        }
        printf("\n");
    }
}
执行耗时5ms,消耗内存380KB