noip2008 双栈排序 (二分图染色)

P1605双栈排序

描述

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

样例输入1[复制]

4
1 3 2 4

样例输出1[复制]

a b a a b b a b

限制

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;
}



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值