【问题描述】

  用两个栈使一个1...n的排列变得有序。一共有四个操作:

  A.stack1.push()   读入一个放入栈一

  B.stack1.pop()   弹出栈一放入输出序列

  C.stack2.push()  读入一个放入栈二

  D.stack2.pop()  弹出栈二放入输出序列

  给你一个初始的排列,求一个字典序最小的操作序列使得变得有序,若没有满足条件的操作序列,输出'0'。

  

  Sample.in                                Sample.out

  4     1 3 2 4                               a b a a b b a b

  4     2 3 4 1                               0

  3     2 3 1                                  a c a b b d

【分析】

  嗯嗯嗯...这题好有意思啊...然后开始埋头苦干,发现好神奇啊!

  1.栈原来真的可以排序诶!

  2.双栈原来也可以排序诶!

  3.原来第二个样例真的不能排序诶!

  ....于是沉默了....于是开始纠结....什么样的数据不能用双栈排序呢?

  ........

  思考了很久很久.....似乎想不出来什么东西....

  不过我在构造中发现....每当一个栈要挂的时候,往往可以祸水东引,将即将堵住的元素放到另一个栈中去,每次两个栈都堵住的时候...恩恩,双栈排序就挂了!

  于是开始想另一个问题:什么样的数据不能用单栈排序....

  随便造了一个:2 3 1 恩,就挂了。

  因为2应该在3前出去,但是因为1在后面,所以2出不去,就被3堵住了....

  所以只要有 i<j<k 满足 a[k]<a[i]<a[j] 就可以卡死一个栈了....

  【题外】【严谨证明】【copy from codevs】【不想看的记住这个结论就好了】

      考虑对于任意两个数a[i]和a[j]来说,它们不能压入同一个栈中的充要条件是什么(注意没有必要使它们同时存在于同一个栈中,只是压入了同一个栈).

      实际上,这个条件p是:存在一个k,使得i<j<k且a[k]<a[i]<a[j].    [猜对了有木有!]

      首先证明充分性,即如果满足条件p,那么这两个数一定不能压入同一个栈.这个结论很显然,使用反证法可证.

    假设这两个数压入了同一个栈,那么在压入a[k]的时候栈内情况如下:

  1.     …a[i]…a[j]…
  2.     因为a[k]比a[i]和a[j]都小,所以很显然,当a[k]没有被弹出的时候,另外两个数也都不能被弹出(否则输出序列中的数字顺序就不是1,2,3,…,n了).
  3.     而之后,无论其它的数字在什么时候被弹出,a[j]总是会在a[i]之前弹出.而a[j]>a[i],这显然是不正确的.
  4.  
  5.       接下来证明必要性.也就是:如果两个数不可以压入同一个栈,那么它们一定满足条件p.
        这里我们来证明它的逆否命题,也就是"如果不满足条件p,那么这两个数一定可以压入同一个栈."
  6.     不满足条件p有两种情况: 1. 对于任意i<j<ka[i]<a[j] , a[k]>a[i]; 2. 对于任意i<j , a[i]>a[j].
  7.       第一种情况下,很显然,在a[k]被压入栈的时候,a[i]已经被弹出栈.那么,a[k]不会对a[j]产生任何影响(这里可能有点乱,因为看起来,当a[j]<a[k]的时候,是会有影响的,但实际上,这还需要另一个数r,满足j<k<ra[r]<a[j]<a[k],也就是证明充分性的时候所说的情况…而事实上我们现在并不考虑这个r,所以说a[k]对a[j]没有影响).
  8.       第二种情况下,我们可以发现这其实就是一个降序序列,所以所有数字都可以压入同一个栈.
  9.     这样,原命题的逆否命题得证,所以原命题得证.
  10.  
  11.     此时,条件pq1[i]和q1[j]不能压入同一个栈的充要条件也得证.

  

  所以如果遇到这样的 i,j,它们一定不能丢到一个栈里面去,于是给它们并查集连个虚点?或者连条什么边[二分图染色]。

  唔...下面就好办啦....[我打的二分图染色,就讲这个哈]

  二分图染色 [只有两种颜色1,0] 就是在每一个连通块上,对一个点染上黑色[belong[x]=1],然后对所有连的边染上和它不同的颜色[belong[v]=1^belong[x]],当然还得给每个元素下一个是否已经找到颜色的标记sure[],以免多次染色和漏掉矛盾。[矛盾的情况很好办咯...就是发现两个已经确定的,而且练了边的点染成了同一个颜色!....然后就枪毙了...]

  因为要求操作序列的字典序最小,所以我们希望前面的元素能尽可能放进栈一中,假设栈一中的都是颜色为黑的,那么只需要按照顺序对每一个不曾染过色的点染成黑色,然后拓展它所在的连通块即可。

  最后,我们都知道了所有点在哪个栈中....就可以开始开心的模拟了!

  记录一个cot表示现在希望弹出的元素是几,发现可以弹出cot的时候一直弹弹弹[ 弹走鱼尾纹 ] 就可以了,具体还不懂就看代码咯.....[有贴心小注释]

  

  

  1. #include<cstdio>
  2. #include<cstring>
  3.  
  4. using namespace std;
  5.  
  6. inline int in(){ //读入优化
  7. int x=;char ch=getchar();
  8. while(ch>'' || ch<'') ch=getchar();
  9. while(ch>='' && ch<='') x=x*+ch-'',ch=getchar();
  10. return x;
  11. }
  12.  
  13. const int maxn=;
  14.  
  15. struct Node{ //携带本人代码气息的邻接表
  16. int data,next;
  17. }node[maxn*maxn];
  18.  
  19. #define now node[point].data
  20. #define then node[point].next
  21.  
  22. int n,cnt,t1,t2,cot=;
  23. int a[maxn],head[maxn],belong[maxn];
  24. int f[maxn],s1[maxn],s2[maxn];
  25. bool sure[maxn],over;
  26.  
  27. inline int Min(int a,int b){
  28. if(a>b) return b;return a;
  29. }
  30.  
  31. inline void add(int u,int v){ //邻接表加边
  32. node[cnt].data=v;node[cnt].next=head[u];head[u]=cnt++;
  33. node[cnt].data=u;node[cnt].next=head[v];head[v]=cnt++;
  34. }
  35.  
  36. void dfs(int x){
  37. sure[x]=true;
  38. for(int point=head[x];point!=-;point=then)
  39. if(sure[now]){ //如果相邻的点已经被染过色
  40. if(belong[now]^belong[x]); //说明相邻两个点的颜色不同
  41. else{over=true;return;}
  42. }
  43. else //如果相邻的点未曾染色
  44. belong[now]=-belong[x],dfs(now); //染色,并扩展所在连通分块
  45. }
  46.  
  47. inline void Pop(){ //弹出栈的函数,因为弹出一个栈,cot会 ++,所以可能会带来连锁弹栈
  48. while(s1[t1]==cot || s2[t2]==cot){
  49. while(s1[t1]==cot && t1)
  50. putchar('b'),putchar(' '),cot++,t1--;
  51. while(s2[t2]==cot && t2)
  52. putchar('d'),putchar(' '),cot++,t2--;
  53. }
  54. }
  55.  
  56. int main(){
  57. n=in();
  58. for(int i=;i<=n;i++) a[i]=in(),head[i]=-;
  59.  
  60. //对于任意两个数a[i]和a[j]来说,它们不能压入同一个栈中的充要条件是 : 存在一个k,使得i<j<k且a[k]<a[i]<a[j]
  61. f[n+]=0x7f7f7f7f;
  62. for(int i=n;i;i--)
  63. f[i]=Min(f[i+],a[i]); //预处理出当前位置往后最小的 a[k]
  64.  
  65. for(int i=;i<n;i++)
  66. for(int j=i+;j<n;j++)
  67. if(a[i]<a[j] && a[i]>f[j+]) add(i,j);
  68.  
  69. for(int i=;i<=n;i++)
  70. if(!sure[i]){
  71. dfs(i); //a[i]所在连通分块中,在序列中的第一个元素染成白色,也就是放入栈 1中[因为 belong[]的初值为 0]
  72. if(over){printf("");return ;}
  73. }
  74.  
  75. for(int i=;i<=n;i++){
  76. if(belong[i]) //模拟压栈
  77. putchar('c'),putchar(' '),s2[++t2]=a[i];
  78. else
  79. putchar('a'),putchar(' '),s1[++t1]=a[i];
  80. if(a[i]==cot) Pop();
  81. }
  82. return ;
  83. }

Noip2008双栈排序的更多相关文章

  1. Luogu1155 NOIP2008 双栈排序 【二分图染色】【模拟】

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

  2. NOIP2008双栈排序[二分图染色|栈|DP]

    题目描述 Tom最近在研究一个有趣的排序问题.如图所示,通过2个栈S1和S2,Tom希望借助以下4种操作实现将输入序列升序排序. 操作a 如果输入序列不为空,将第一个元素压入栈S1 操作b 如果栈S1 ...

  3. noip2008 双栈排序

    题目描述 Description \(Tom\)最近在研究一个有趣的排序问题.如图所示,通过\(2\)个栈\(S_1\)和\(S_2\),\(Tom\)希望借助以下\(4\)种操作实现将输入序列升序排 ...

  4. NOIP2008双栈排序(贪心)

    题目描述 Tom最近在研究一个有趣的排序问题.如图所示,通过2个栈S1和S2,Tom希望借助以下4种操作实现将输入序列升序排序. 操作a 如果输入序列不为空,将第一个元素压入栈S1 操作b 如果栈S1 ...

  5. [题解] [NOIP2008] 双栈排序——关系的冲突至图论解法

    Problem 题目描述 Tom最近在研究一个有趣的排序问题.如图所示,通过2个栈S1和S2,Tom希望借助以下4种操作实现将输入序列升序排序. 操作a 如果输入序列不为空,将第一个元素压入栈S1 操 ...

  6. [NOIP2008]双栈排序 【二分图 + 模拟】

    题目描述 Tom最近在研究一个有趣的排序问题.如图所示,通过2个栈S1和S2,Tom希望借助以下4种操作实现将输入序列升序排序. 操作a 如果输入序列不为空,将第一个元素压入栈S1 操作b 如果栈S1 ...

  7. [luogu1155 NOIP2008] 双栈排序 (二分图染色)

    传送门 Description Input 第一行是一个整数 n . 第二行有 n 个用空格隔开的正整数,构成一个 1−n 的排列. Output 共一行,如果输入的排列不是"可双栈排序排列 ...

  8. $[NOIp2008]$双栈排序 栈/二分图/贪心

    \(Sol\) 先考虑单栈排序,怎么样的序列可以单栈排序呢?设\(a_i\)表示位置\(i\)是哪个数.\(\exist i<j<k\),都没有\(a_k<a_i<a_j\), ...

  9. [NOIp2008] 双栈排序 (二分图染色 + 贪心)

    题意 给你一个长为 \(n\) 的序列 \(p\) ,问是否能够通过对于两个栈进行 push, pop(print) 操作使得最后输出序列单调递增(即为 \(1 \cdots n\) ),如果无解输出 ...

随机推荐

  1. (转)Java操作Hbase进行建表、删表以及对数据进行增删改查,条件查询

    1.搭建环境 新建JAVA项目,添加的包有: 有关Hadoop的hadoop-core-0.20.204.0.jar 有关Hbase的hbase-0.90.4.jar.hbase-0.90.4-tes ...

  2. 【原创】解决鼠标经过子元素触发mouseout,mouseover事件的问题

    关键词:父子元素关系  mouseout  mouseover  事件  事件冒泡 初期代码: <!DOCTYPE html> <html> <head> < ...

  3. 老外写的在桌面添加快捷方式(DELPHI XE5 ANDROID)

    UsesAndroidapi.JNI.GraphicsContentViewText, FMX.Helpers.Android,Androidapi.JNI.JavaTypes, FMX.Platfo ...

  4. xml结构

    一.XmlHelper using System; using System.Collections.Generic; using System.Linq; using System.Web; usi ...

  5. 中兴软件编程规范C/C++

    Q/ZX 深圳市中兴通讯股份有限公司企业标准 (设计技术标准) Q/ZX 04.302.1–2003      软件编程规范C/C++                               20 ...

  6. python ssh

    使用python包paramiko实现通过ssh的安全远程访问 使用pip下载安装paramiko,提示会缺一个crypto包,用pip将这个包也安好,python就可以正常引用paramiko了 一 ...

  7. [div+css布局]命名规则

    //首页可能碰到的 页头:header登录条:loginBar标志:logo侧栏:sideBar广告:banner导航:nav子导航:subNav菜单:menu子菜单:subMenu搜索:search ...

  8. PHP 学习笔记 01

    例子: 为什么要学PHP 主观原因: 前段时间在学校处理了毕业的一些事情,回到上海后开始了找工作的旅程.意向工作是WPF开发或者ASP.NET 作为后端的WEB开发. 陆陆续续一直在面试,其中有一家公 ...

  9. Linux中JDK环境变量的配置

    在JDK安装好以后,需要进行环境变量的配置 配置目录   /etc/profile 在这个文件的末尾追加 JAVA_HOME=/home/j2sdk1.4.2_11PATH=$PATH:/home/j ...

  10. DB2行转列(多维度)

    多维度下进行行列转换,下面的行列转换时根据客户,所属银行机构进行的行列转换. -----------------建表 CREATE TABLE CUST_BANK_INFO ( CUST_ID ), ...