来源:牛客网

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 131072K,其他语言262144K
64bit IO Format: %lld

题目描述

Tom最近在研究一个有趣的排序问题。如图所示,通过2个栈S1和S2,Tom希望借助以下4种操作实现将输入序列升序排序。

操作a:如果输入序列不为空,将第一个元素压入栈S1
操作b:如果栈S1不为空,将S1栈顶元素弹出至输出序列

操作c:如果输入序列不为空,将第一个元素压入栈S2

操作d:如果栈S2不为空,将S2栈顶元素弹出至输出序列

如果一个1~n的排列P可以通过一系列操作使得输出序列为1,2,…,(n-1),n,Tom就称P是一个“可双栈排序排列”。例如(1,3,2,4)就是一个“可双栈排序序列”,而(2,3,4,1)不是。下图描述了一个将(1,3,2,4)排序的操作序列:<a,c,c,b,a,d,d,b>

当然,这样的操作序列有可能有几个,对于上例(1,3,2,4),<a,c,c,b,a,d,d,b>是另外一个可行的操作序列。Tom希望知道其中字典序最小的操作序列是什么。

输入描述:
第一行是一个整数n。
第二行有n个用空格隔开的正整数,构成一个1~n的排列
输出描述:
共一行,如果输入的排列不是“可双栈排序排列”,输出数字0;否则输出字典序最小的操作序列,每两个操作之间用空格隔开,行尾没有空格。
示例1
输入
复制

4
1 3 2 4

输出
复制

a b a a b b a b

示例2
输入
复制

4
2 3 4 1

输出
复制

0

示例3
输入
复制

3
2 3 1

输出
复制

a c a b b d

备注:

30%的数据满足:n<=10
50%的数据满足:n<=50
100%的数据满足:n<=1000

题意:

有四种操作,问如何操作可以实现将输入序列升序排序
四种操作分别是:
操作a:如果输入序列不为空,将第一个元素压入栈S1
操作b:如果栈S1不为空,将S1栈顶元素弹出至输出序列
操作c:如果输入序列不为空,将第一个元素压入栈S2
操作d:如果栈S2不为空,将S2栈顶元素弹出至输出序列

题解:

貌似二分图可以做(不过我还没看懂)
首先推出规律:

如果两个数 i,j(i≤j)i,j(i≤j) 不能被放入同一个栈中,当且仅当存在 k,k>jk,k>j, 且 q[k]<q[i]<q[j]q[k]<q[i]<q[j]。

现在有两个栈,我们只需要将满足这个条件的点各自归到一边,中间连一条线,用经典的染色法判断是否为二分图,若是则按照颜色入栈,
若不是则说明不能完成

代码:

#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<stack>
#include<cmath>
#define maxn 1004
using namespace std;

const int inf=19260817;
int n,num;
int color[maxn];
int t[maxn];    //要排序的元素的存储
int s[maxn];    //判断两个数字是否满足规则
bool flag,e[maxn][maxn];

void paint(int x,int c){
   	//DFS进行染色
    color[x]=c;
    for(int i=1;i<=n;i++){
   
        if(e[x][i]){
       //查找相邻点 
            if(color[i]==c) flag=false;		//若相邻点颜色相同,则错误
            if(!color[i]) paint(i,3-c);		//若未染过色,对其染色,3-c结果为1,2,表示1与2号栈
        }
    }
}

void make(){
   	//创造二分图
    s[n+1]=inf; 	
    for(int i=n;i>=1;i--){
   
        s[i]=t[i];
        if(s[i+1]<s[i])
            s[i]=s[i+1];
    }
    for(int i=1;i<n;i++){
   
        for(int j=i+1;j<n;j++){
   
            if(t[i]<t[j] && s[j+1]<t[i]){
   
                e[i][j]=e[j][i]=1;  //按规则创建图
            }
        }
    }
    for(int i=1;i<=n;i++){
    
        if(!color[i]){
    		//染色
            paint(i,1);
        }
    }
}

void work(){
   
    if(flag==false){
   
        printf("0\n");
        return ;        
    }
    stack<int> stack1,stack2;
    int now=1;
    for(int i=1;i<=n;i++){
   
        if(color[i]==1){
    	//入栈
            stack1.push(t[i]);
            printf("a ");            
        }
        else {
   
            stack2.push(t[i]);
            printf("c ");               
        }
        while((!stack1.empty() && stack1.top()==now) || 
              (!stack2.empty() && stack2.top()==now)){
    	//判断是否弹出
            if(!stack1.empty() && stack1.top()==now){
   
                stack1.pop();
                now++;
                printf("b ");
            }
            else{
   
                stack2.pop();
                now++;
                printf("d ");                
            }
        }
    }
}

int main(){
   
    flag=1;
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
   
        scanf("%d",&t[i]);
    }
    make();
    work();   
    return 0;
}