双栈排序

 

 

by  GeneralLiu

 

NOIP2008 双栈排序 

 

题目大意

给出一个1~n的排列

能否通过两个栈的出栈进栈操作

完成从大到小排序

如果不能 输出 0

能就 输出字典序最小的操作方案

(题目要求详情见洛谷链接  没错,就是上面那个)

 

假设现在你已经了解 题目的要求 以及细节

 

那我就在博客 try 着去重现

培训时 mzx dalao 讲课时的情景

选中下文查看分析

设 a<b<c<d...<n ;也可以理解为 a=1,b=2 , c=3.....

  “ 先不要管 双栈排序

  先想想 单栈排序

  (一个 稍微凌乱点的 序列能单栈,

    就是 " b a d c... n "这种不是太乱的 

  当然,大多数情况是不能单栈排序的,

  (就是出现形如 " b c a " 这种又乱了些序列时

  当单栈排序行不通时

  就试试双栈排序

  (拿上一个" b c a"来讲

    如果双栈 可以 b进栈1 c进栈2 a进栈1 a出栈1 b出 c出...

   这种不是很恶心的 序列 是可以双栈的)

  那双栈什么时候也会虚呢?

  (其实双栈很容易虚的

    不信 把上一个" b c a "加一个d

    变成" b c d a "试试

    点到为止)

  现在对于本题来说已经可以了

  (不能双栈直接 输出 NO

    能的话 优先 栈1 字典序最小嘛)

  但还是拓展一下

  双栈虚了,三栈行不?

  (咦,刚才的 " b c d a "用三栈可以耶

    但再加一个 e 的话

    就是 " b c d e a "

    那么三栈也虚了)

  ......”

好了 “聪明的读者,看到这你一定明白了”by 刘汝佳

 

假设有 a b c d e 五个数

出现  b c d a 时 双栈不能解决 可三栈能

出现  b c d e a 时三栈就不能了

到这儿,规律已出

现在 能否用 N 栈排序的问题已经解决

那么就剩字典序最小的问题了

其实贪心去优先用第一个栈就好

 

现在思路已经 缕出

至于代码实现

分两步:

1:判断能否双栈

  维护一个mind[]数组  mind[i] 表示末尾 i 个数的最小值 // 代码 24行

  若 满足一个 i<j<k && s[k]<s[i]<s[j] //代码 25~28 行

  说明 第 i 和第 j 个不能单栈 

  就 把 i 到 j 连一条边 表示 i ,j  不能同栈

  最后抽象成 二分图判断 即可 // 代码 29~31行

  

2:输出操作序列

   依题目要求即可

  不必细说

  看代码 

 

代码

 

 1 #include<iostream>
 2 #include<stack>
 3 #include<algorithm>
 4 #define N 1005
 5 using namespace std;
 6 int mind[N],n,s[N],color[N],mp[N][N];
 7 stack<int> s1,s2;//两个栈 
 8 void dfs(int u,int c){ // 二分图 染色 
 9     color[u]=c;
10     for(int i=1;i<=n;i++)
11       if(mp[u][i]){
12           if(color[i]==c){ // 不能双栈 退出 
13               cout<<0;
14               exit(0);
15           }
16           if(!color[i])
17             dfs(i,-c);
18       }
19 }
20 int main(){
21     cin>>n;
22     for(int i=1;i<=n;i++)cin>>s[i];
23     mind[n+1]=N;
24     for(int i=n;i>=1;i--)mind[i]=min(mind[i+1],s[i]);//维护mind[] 
25     for(int i=1;i<n;i++)  // 枚举 判断 能否单栈 
26       for(int j=i+1;j<=n;j++)
27         if(s[i]<s[j]&&mind[j+1]<s[i]) 
28           mp[i][j]=mp[j][i]=1; // 连边 
29     for(int i=1;i<=n;i++)
30       if(!color[i])
31         dfs(i,1);// 优先 染色 为1 进栈1 保证 字典序 最小 
32     int aim=1; // 表示 目前应该是 该aim 出栈 (因为是个排列嘛) 
33     for(int i=1;i<=n;i++){ // n 次进栈 
34         if(color[i]==1){
35             cout<<"a ";
36             s1.push(s[i]);
37         }
38         else{
39             cout<<"c ";
40             s2.push(s[i]);
41         }
42         // 该出栈时 就出栈 诶~ 
43         while( (!s1.empty()&&s1.top()==aim) || (!s2.empty()&&s2.top()==aim) ){
44             if(!s1.empty()&&s1.top()==aim){
45                 cout<<"b ";
46                 s1.pop();
47             }
48             else{
49                 cout<<"d ";
50                 s2.pop();
51             }
52             aim++; // 出一个 加一个 
53         }
54     }
55     return 0;
56 }

 

posted @ 2017-05-09 08:33  GeneralLiu  阅读(556)  评论(0编辑  收藏  举报