noip2008 双栈排序

描述

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;
否则输出字典序最小的操作序列,每两个操作之间用空格隔开,行尾没有空格。

数据范围

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

------------------------------------------------------------------------

 正解=图匹配Orz。。

先考虑单栈排序

 显然这是个由下到上的递减栈。

 能单栈排序的情况: 不存在 (i<j<k 且  v[k]<v[i]<v[j])时即可进行排序

 必要性 :

     当 j 要进栈时,i 必须出栈,但 k 又必须在 i 前出栈,显然不可以- =

 充分性:

     当 i<j<k 时除上述情况还有

        A : 当 v[i]>v[j] 时

             i ,j可以同时在栈中,无论v[k]的值如何,都能进行排序(显然)

        B: 当 v[i]<v[j]<v[k ]时

             也显然可以排序- =

        C:当 v[i]<v[k]<v[j]时

             好像也显然可以排序- =

     所以显然除 (i<j<k 且  v[k]<v[i]<v[j])都可进行排序 - =

  其实也可以像ak大神(Orz)一样,拿1 2 3举例证明,虽不怎么全面,但很好理解。

考虑完单栈回到双栈排序

   就像归并一样,两个都能排序,那合起来也能排序。

   如果存在(i<j<k 且  v[k]<v[i]<v[j]))时i , j ,就不能在同一栈里。

   预处理i ,j (i<j)如果存在上述情况,那他们就必然不在同一个栈里,在i,j间连一条线

   做一遍染色即可,出现非法情况(同一点染上不同颜色)就无解。

   让完后做一个简单的字典序最小进栈出栈操作,记下当前要出栈的数,然后依题意搞之即可(令人蛋疼- =)。

 代码如下:

ContractedBlock.gif ExpandedBlockStart.gif
 1 #include<cstring>
 2 #include<algorithm>
 3 #include<cstdio>
 4 #include<string>
 5 #include<iostream>
 6 #include<queue>
 7 #include<stack>
 8 #define INF 99999999
 9 #define LL long long
10 using namespace std;
11 int col[1001],next[10010],last[10010],s[10010],T,n,v[1001],K[1001],ans[3000];
12 stack<int> q[3];
13 void addedge(int x,int y){
14     next[++T]=last[x]; last[x]=T; s[T]=y;
15     next[++T]=last[y]; last[y]=T; s[T]=x; 
16 }
17 void DFS(int now,int c){
18     if(!col[now]) col[now]=c; 
19     else if(col[now]!=c){
20         printf("0");
21         exit(0);
22     } else return ;
23     for(int i=last[now];i;i=next[i]) DFS(s[i],3-c);
24 }
25 int main(){
26     scanf("%d",&n);
27     for(int i=1;i<=n;i++) scanf("%d",&v[i]);
28     K[n]=v[n];
29     for(int i=n-1;i;i--) K[i]=min(K[i+1],v[i]);
30     for(int i=1;i<=n;i++)
31      for(int j=i+1;j<n;j++)
32       if(K[j+1]<v[i]&&v[j]>v[i])
33        addedge(i,j);
34     for(int i=1;i<=n;i++) if(!col[i]) DFS(i,1);
35     int now=1;
36     int T=1;
37     while(1){
38         if(now>n) break;
39         if(col[T]==1&&(q[1].empty()||q[1].top()>v[T])){
40             q[1].push(v[T]);
41             ++T;
42             printf("a ");
43             continue ;
44         }
45         if(!q[1].empty()&&q[1].top()==now) {
46             printf("b "); 
47             q[1].pop();
48             ++now;
49             continue ;
50         }
51         if(col[T]==2&&(q[2].empty()||q[2].top()>v[T])){
52             q[2].push(v[T]);
53             ++T;
54             printf("c ");
55             continue ;
56         }
57         if(!q[2].empty()&&q[2].top()==now) {
58             printf("d "); 
59             q[2].pop();
60             ++now;
61             continue ;
62         }
63     }
64 }
View Code

转载于:https://www.cnblogs.com/Blacko/p/3373313.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值