描述
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;
否则输出字典序最小的操作序列,每两个操作之间用空格隔开,行尾没有空格。
限制
1s
提示
30%的数据满足: n<=10
50%的数据满足: n<=50
100%的数据满足: n<=1000
来源
NOIP2008 提高组第4题
解析:如果存在 a[k]<a[i]<a[j] (i<j<k),那么a[i]与a[j]就不能放进同一个栈中。据此,进行染色,对每个数字a[i],记录不能与它在同一个栈中的数字。
用b[j]记录[j,n]区间内的最小值,则:b[j]=min(a[j],b[j+1]),上诉不等式转换为:b[j+1]<a[i]<a[j]。
接下来,就是枚举a[i],如果a[i]能放入一号栈,则放入;否则,放入二号栈。如果a[i]是当前应当输出的数,则输出a[i]。
代码:
#include<cstdio>
#include<algorithm>
#define maxn 1000
using namespace std;
int n,q[maxn+20];
int f[maxn+20],b[maxn+20];
int a[maxn+20][maxn+20];
int s1[maxn+20],s2[maxn+20];
int sum1=0,sum2=0;
char s3[maxn*1000+20];
bool dfs(int x)
{
int i,j,k;
for(i=1;i<=a[x][0];i++)
{
if(f[a[x][i]]==f[x])return 0;
if(f[a[x][i]]==0)
{
f[a[x][i]]=3-f[x];
dfs(a[x][i]);
}
}
return 1;
}
int main()
{
int i,j,k;
scanf("%d",&n);
for(i=1;i<=n;i++)scanf("%d",&q[i]);
for(b[n]=q[n],i=n-1;i>=1;i--)b[i]=min(q[i],b[i+1]);
for(i=1;i<n;i++)
for(j=i+1;j<n;j++)
if(q[i]<q[j] && b[j+1]<q[i])
{
a[i][++a[i][0]]=j;
a[j][++a[j][0]]=i;
}
for(i=1;i<=n;i++)
{
if(f[i]==0)f[i]=1;
if(!dfs(i))
{
printf("0\n");
return 0;
}
}
for(i=1;i<=n;i++)
{
if(f[i]==1)
{
s1[++s1[0]]=q[i];
s3[++sum1]='a';
}
else
{
s2[++s2[0]]=q[i];
s3[++sum1]='c';
}
while(1)
{
if(s1[s1[0]]==sum2+1)
{
s3[++sum1]='b',sum2++,s1[0]--;
continue;
}
if(s2[s2[0]]==sum2+1)
{
s3[++sum1]='d',sum2++,s2[0]--;
continue;
}
break;
}
}
for(i=1;i<sum1;i++)printf("%c ",s3[i]);
printf("%c\n",s3[sum1]);
return 0;
}