[关闭]
@rebirth1120 2019-07-17T19:21:35.000000Z 字数 2430 阅读 1067

[NOIP2008]双栈排序

NOIP 解题记录 二分图


参考资料:[NOIp2008] 双栈排序 (二分图染色 + 贪心) 题解
题目链接:[NOIP2008]双栈排序

题目大意:

有一个长度为 的序列(是 的排列),现在有 个栈 个操作。

操作 如果输入序列不为空,将第一个元素压入栈
操作 如果栈 不为空,将 栈顶元素弹出至输出序列
操作 如果输入序列不为空,将第一个元素压入栈
操作 如果栈 不为空,将 栈顶元素弹出至输出序列

判断是否能使得输出序列升序排序,
若不能,则输出
若能,则输出字典序最小的操作序列。

解题思路:

首先思考如何判断是否能使得输出序列升序排序。

考虑三个原序列的下标 ,满足
,则 不能进入同一个栈中。

原因:

进入同一个栈中,由于 要比 先出栈;
而由于 ,所以 要比 先出栈, 若要出栈,首先要进栈 废话 若要进栈,那么 就要先进栈,就会使得 先出栈,就产生了矛盾。

为了判断这个矛盾是否会导致无法排序,我们选择使用二分图染色,
在有矛盾的 之间连一条边(无向边),然后对这张图进行染色,若不是二分图,则无法排序,输出 0。


判断完了是否能正确排序后,我们就可以思考如何取字典序最小的操作序列了。

首先考虑怎么使操作是正确的,即操作后能产生升序序列。
大体思路就是将序列中的每个数依次压入它属于的栈中(属于哪个栈在二分图染色时就可以标记好),在将这个数压入栈前,我们要先判断压入之后栈是否仍然单调,若不单调,则一直弹出栈顶元素,直到单调为止。
注意:在弹出栈顶元素时,栈顶的元素可能不是当前应当弹出的数(因为我们要使得输出序列的元素是递增的),所以我们需要一个变量 ,表示当前应当弹出的数,若该栈顶的数不等于 ,就弹出另外一个栈顶的数。

在考虑以上条件之后,我们就可以输出正确的操作序列了,那要怎么才能输出字典序最小的呢?
同一个栈的压入和弹出的相对顺序似乎改变不了(好像是吧),那么我们就考虑两个栈的操作之间的顺序吧。
既然 的操作的字典序更小,那么我们可以想一下什么时候能先进行 的操作。由于压栈的顺序是一定的,那么我们就考虑弹出操作,在压入属于 的数之前,我们可以先把 中能弹出的数都弹出来,这样就可以使得字典序最小了。

说是这么说,但这道题我一点思路都没有,全都是看题解写出来的……

代码实现

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<vector>
  4. #include<queue>
  5. using namespace std;
  6. const int N=1000+7;
  7. int n,val[N],minx[N],col[N];
  8. bool flag;
  9. vector<int> p[N];
  10. void init(){
  11. cin>>n;
  12. for(int i=1;i<=n;i++)
  13. scanf("%d",&val[i]);
  14. minx[n+1]=n+1;
  15. for(int i=n;i>=1;i--)
  16. minx[i]=min(minx[i+1],val[i]);
  17. for(int i=1;i<=n;i++)
  18. for(int j=i+1;j<=n;j++)
  19. if(val[i]<val[j]&&minx[j+1]<val[i]) p[i].push_back(j),p[j].push_back(i);
  20. }
  21. void run(){
  22. for(int i=1;i<=n;i++)
  23. if(!col[i]){
  24. queue<int> q;
  25. col[i]=1;
  26. q.push(i);
  27. while(!q.empty()){
  28. int u=q.front();
  29. q.pop();
  30. for(int j=0;j<p[u].size();j++){
  31. int v=p[u][j];
  32. if(col[v]){
  33. if(col[v]==col[u]){
  34. flag=1;
  35. return;
  36. }
  37. continue;
  38. }
  39. col[v]=3-col[u];
  40. q.push(v);
  41. }
  42. }
  43. }
  44. }
  45. struct stack{
  46. int a[N],p=0;
  47. int top(){ return a[p]; }
  48. bool empty(){ return p==0; }
  49. void pop(){ if(p) p--;}
  50. void push(int x){ a[++p]=x; }
  51. }s[3];
  52. int now;
  53. bool judge(int id){
  54. if(s[id].empty()||s[id].top()!=now+1) return 0;
  55. return 1;
  56. }
  57. void Pop(int id){
  58. now++;
  59. s[id].pop();
  60. putchar(id==1 ?'b' :'d');
  61. putchar(' ');
  62. }
  63. void Push(int v,int id){
  64. if(id==2) while(judge(1)) Pop(1);
  65. while(!s[id].empty()&&s[id].top()<v){
  66. if(!judge(id)) Pop(3-id);
  67. else Pop(id);
  68. }
  69. if(id==2) while(judge(1)) Pop(1);
  70. s[id].push(v);
  71. putchar(id==1 ?'a' :'c');
  72. putchar(' ');
  73. }
  74. void print(){
  75. for(int i=1;i<=n;i++){
  76. Push(val[i],col[i]);
  77. }
  78. while(!s[1].empty()){
  79. if(!judge(1)) Pop(2);
  80. else Pop(1);
  81. }
  82. while(!s[2].empty()) Pop(2);
  83. }
  84. int main(){
  85. //freopen("1.txt","r",stdin);
  86. //freopen("2.txt","w",stdout);
  87. init();
  88. run();
  89. if(flag){
  90. printf("0");
  91. return 0;
  92. }
  93. print();
  94. return 0;
  95. }
  96. /*
  97. 4
  98. 1 3 2 4
  99. */
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注