Problem

题目描述

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希望知道其中字典序最小的操作序列是什么。

Solution

这题我一开始想的是贪心。但后来发现a和c的入栈顺序有误可能会导致无解。

然后我看了一下标签——“图论”?嗯???

我看了几个题解。二分图。i与j不能放在一起。贪心。

然后我再试着打了打代码,A了,证明我的思路是正确的。感谢那些题解提供的帮助,虽然我没有完全看懂。

这是一道很巧妙的题目,用了一个我认为很重要的思想。

思路零(最开始的思路)

经过了一些考虑的贪心。

第一,我们如果入这个栈的数是目前排序到的最大的数+1,那么这个栈我们是不是不能再入栈?这是显然的,在这个栈的下一次进栈前,我们应当将这个元素出栈。

第二,否则能入栈则入栈,尽量入a栈。

第三,栈顶的元素必须比入栈的元素要大,正确性显然。

考虑执行顺序b->a->d->c。检查到这种操作能执行则执行。显然这种执行顺序在贪心的求字典序最小上是正确的。

然后这个贪心当然是错误的啦

思路一(来自某题解)

我们先考虑一个栈能不能排。如果一个栈能排的话我们还会用两个栈吗?按照贪心原则,这么做肯定是不行的。

那么问题来了——什么时候我们得必须用两个栈才能排?

引理(来自同样的题解和另一个题解)

在排列P中。若P[k]<P[i]<P[j],i<j<k,则i和j不能在同一个栈中。正确性不是很显然,感性理解也是可以的。证明可以去网上找其它的题解。想要理解的话不如自己拿个例子试试,比如(2,3,1)。

思路二(来自某题解)

这道题到底跟图论有什么关系?最大的关系在于“双”这个字。

二分图可以用来表示关系的冲突。比如两个相邻的点不能染成同样的颜色,又或是两个数不能放进同一个栈中。知道了这个关系,我们可以知道两个数应该放在什么栈里,知道到底存不存在方案使得排序完成。

我们将这道题转换成二分图进行处理。

枚举\(i<j<n\),若j+1~n中存在k使得\(P[k]<P[i]<P[j]\),则i和j连一条边,代表他们不能入同一个栈。

显然若i与j没有连边,就代表他们按顺序排始终可以出现在同一个栈中。

这个过程可以用一个后缀求最小值来\(O(n^{2})\)地求得。

改进后的贪心思路(建立在求得的二分图的基础上)

显然我们现在将图染成了两种颜色。若颜色不同则代表它得入不同的栈。

那么我们考虑一下,顺序是ABC,A是0,B是1,C是0。那么AC肯定入a栈,B入c栈对不对?因为A是最先的。在操作顺序aca和cac中,我们选择前一个操作更好。所以我们可以得到颜色与第一个数同色的点入a栈,其余入c栈。

在入栈的基础上,我们再使用上方的贪心模拟,按照b->a->d->c的操作顺序考虑。显然这种顺序是最优的。并且根据得到的二分图入栈,可以保证顺序是正确的。保证顺序正确得到可行解的同时字典序最小,那么就是题目要求得的解。

何为无解的情况?就是无法构造二分图的情况。即A-B B-C C-A的情况(A-B:A不能和B放在一起)。

个人觉得这题是道很好的拓宽思路的题目2333

Code

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. const int N=1010,M=200010;
  4. int P[N];
  5. int h[N],to[M],nexp[M],p=1;
  6. inline void ins(int a,int b){
  7. nexp[p]=h[a],h[a]=p,to[p]=b,p++;
  8. }
  9. int sm[N];
  10. int color[N];
  11. bool imp;
  12. void dfs(int x,int c){
  13. color[x]=c;
  14. for(int u=h[x];!imp && u;u=nexp[u]){
  15. if(color[to[u]]==-1) dfs(to[u],c^1);
  16. else if(color[x]==color[to[u]]) imp=true;
  17. }
  18. }
  19. int s1[N],t1=1;
  20. int s2[N],t2=1;
  21. int now;
  22. int main(){
  23. ios::sync_with_stdio(false);
  24. int n;
  25. cin>>n;
  26. for(int i=1;i<=n;i++) cin>>P[i];
  27. sm[n]=P[n];
  28. for(int i=n-1;i>=1;i--) sm[i]=min(sm[i+1],P[i]); // 后缀最小值
  29. for(int i=1;i<=n;i++)
  30. for(int j=i+1;j<n;j++)
  31. if(sm[j+1]<P[i]&&P[i]<P[j]) ins(i,j),ins(j,i); // 建图
  32. memset(color,-1,sizeof(color));
  33. for(int i=1;i<=n;i++){
  34. if(color[i]==-1) dfs(i,0); // 进行染色
  35. if(imp) printf("0\n"),exit(0); // 无解则退出
  36. }
  37. int i=1;
  38. now=1;
  39. while(now<=n){
  40. if(s1[t1-1]==now)t1--,now++,printf("b ");
  41. else if(color[i]==0)s1[t1++]=P[i++],printf("a ");
  42. else if(s2[t2-1]==now)t2--,now++,printf("d ");
  43. else if(color[i]==1)s2[t2++]=P[i++],printf("c ");
  44. } // 进行贪心的模拟
  45. return 0;
  46. }

[题解] [NOIP2008] 双栈排序——关系的冲突至图论解法的更多相关文章

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

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

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

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

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

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

  4. noip2008 双栈排序

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

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

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

  6. Noip2008双栈排序

    [问题描述] 用两个栈使一个1...n的排列变得有序.一共有四个操作: A.stack1.push() 读入一个放入栈一 B.stack1.pop() 弹出栈一放入输出序列 C.stack2.push ...

  7. NOIP2008双栈排序(贪心)

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

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

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

  9. Luogu1155 NOIP2008双栈排序(并查集)

    两个位置i和j上的元素不能被放进同一个栈的充要条件显然是存在k使i<j<k且ak<ai<aj.由此在保证合法的情况下贪心地放就是正确的了. 至于如何判断,可以记一下后缀最小值, ...

随机推荐

  1. OpenDayLight Helium实验三 OpenDaylight二层转发机制实验

    本文基于OpenDaylight二层转发机制实验 而成 在SDN网络中,处于末端的主机并不知道其连接的网络是SDN,某台主机要发送数据包到另一台主机,仍然需要进行IP到MAC地址的ARP解析.SDN网 ...

  2. 解决QML Window 增加radius效果

    做开发时,突然遇到 一个需要模态展示的对话框,做出来后,发现还要radius属性,增加时发现,Window控件不支持这个属性.如果是以前,原本就打算放弃了,但想一下,这种应该是支持的,既然接口上没有, ...

  3. bzoj 1799: [Ahoi2009]self 同类分布 数位dp

    1799: [Ahoi2009]self 同类分布 Time Limit: 50 Sec  Memory Limit: 64 MB[Submit][Status][Discuss] Descripti ...

  4. python 生成zip压缩包

    import zipfile file_name="a.txt" f = zipfile.ZipFile('test.zip','w',zipfile.ZIP_STORED) f. ...

  5. 用 profvis 进行性能分析

    Rprof( ) 函数提供了有用的信息帮助我们找到代码瓶颈,进而提升代码性能.RStudio 也发布了一个增强版的分析工具 profvis( ),它还提供了用于分析 R 代码的交互式可视化功能(htt ...

  6. redis的过期策略以及内存淘汰机制

    redis采用的是定期删除+惰性删除策略. 为什么不用定时删除策略? 定时删除,用一个定时器来负责监视key,过期则自动删除.虽然内存及时释放,但是十分消耗CPU资源.在大并发请求下,CPU要将时间应 ...

  7. SpringBoot.资料

    1.du 搜 "springboot 视频" 2.SpringBoot视频教程_哔哩哔哩 (゜-゜)つロ 干杯_-bilibili.html(https://www.bilibil ...

  8. 一.UTF8字符集csv文件在oracle下乱码问题处理

    1.问题描述 在excel中生成了一个UTF-8编码格式的csv文件准备导入数据库,在notpad++下打开显示正常,编码集为UTF-8,通过pl/sql dev导入oracle是出现乱码,此时初步推 ...

  9. Lua 中的 function、closure、upvalue

    Lua 中的 function.closure.upvalue function,local,upvalue,closure 参考: Lua基础 语句 lua学习笔记之Lua的function.clo ...

  10. 解决ubuntu登陆失败,"Failed to start session"的问题

    我是在虚拟机中安装了Ubuntu 14.04 系统,在Ubuntu 中执行 apt-get update  和  apt-get upgrade 命令后,然后重启系统.    但是,在重启成功后,在登 ...