zoukankan      html  css  js  c++  java
  • NOIP 2008 提高组 双栈排序(vijos1605)(一种“科学”的打法)


          附:   题目链接                vijos 1605                                        地址:https://www.vijos.org/p/1605


      思路:

      引:将问题先简化为单栈排序,因为双栈排序也就是两个单栈同时进行排序,要考虑的是:哪些点放在一个栈里面排序;

      一、所以先来考虑单栈排序:

          在栈的时候,有提到:栈是先进后出的 (当然,指的是在它出栈之前如果有元素在它上面,则它是后出的),

        所以就有:对于 i < j ,设 p [ x ]  为第 x 个进栈的元素,如果p[ i ] < p[ j ] 也就是说:按照题目要求的按顺序出栈,小的

        要先出,p[ i ]要先出,所以 p[ i ]  必须在 p[ j ]进栈前出栈,不然 p[ i ]必须在p[ j ] 出栈后才能出,这样就不能满足按顺序,

               但同样的,p[ i ]能够出栈的前提是: 所有比它小的元素均已出栈;这时就能发现,如果在 j之后存在比 p[ i ] 小的元素,则单

        栈排序不可进行。

          并且只要不存在这种情况,则单栈排序一定能进行,因为对于任意 p[ x ] 在它必须出栈的时候不存在限制它出栈的元素,然

        后按照进栈顺序进行模拟就可完成排序过程。

      二、现在再来看看双栈排序:

          在写代码之前,必须明确的是:双栈排序就是同时给出两个单栈的进栈顺序,并且没告知哪些元素是一个栈的,哪些元素不是

        一个栈的,所以代码的首要任务就是对两个单栈进行元素的分配,并且是两个单栈都能进行排序的分配,如果找不出就输出零。

          联系第一步的思考,就是说对于每一对如上述所说的点,如果上述情况存在,那么这两个点不能在同一个单栈里面,所以就可

        以通过染色对栈进行分配。

      三、完成分配后:

          基本就是纯模拟了,同时模拟两个栈的操作,按颜色分成两个栈,然后按 操作 a > 操作 b > 操作 c > 操作 d 的优先级进行

        贪心模拟。

          注:本代码用的不是这种一个个操作去贪心的科学模拟方法(—> _ —>)而是用了一种极不科学的打法(甚至连下标都不小

        心打错的打法)但是对于任何随机数据和科学打法的结果却是一样一样的— —!  并且vijos上总耗时0毫秒AC— —!至今不知为

        何如此拙计— —! 


     

       四、算法实现:

          将所有冲突的元素连上一条线(用邻接表、哈希神马都可以,线不会多到那里去,本代码采用哈希),全部连完后,从第一个

        点开始到第n 个点,如果没染色就对它进行染色,并把与它矛盾的点染上另一个颜色,如果染色过程中出现将一个有颜色的点染成

        另一种颜色的情况,那么就无解,因为至少有其中一个单栈无法进行排序。(其实染色的过程就是在解决最后问题中对元素的分配

        )。

          最后用贪心模拟一遍就O拉~


       五、代码:

          I、 无注释的压行压得有点丧心病狂的33行代码~~~其实也没多丧心病狂— —!函数过程之间的空行还是有的— —!定义全

        局变量时数组的变量也分两行了— —:

          

          
     1 #include<cstdio> 
     2 #include<algorithm> 
     3 int p  [1010 ],col[1010 ],Min[1010 ],pai[3][1010],l[3],wei[10010],las[10010],too[10010];
     4 int n,t=0,should=1;
     5 
     6 void line(int x,int y) {
     7   las[++t] = wei[x]; wei[x] = t; too[t] = y;
     8   las[++t] = wei[y]; wei[y] = t; too[t] = x;
     9 }
    10 
    11 void draw(int x,int c) {
    12   if (!col[x]   )  col[x]=c;             else
    13   if ( col[x]!=c) {printf("0");exit(0);} else return;
    14   for (int i=wei[x]; i; i=las[i]) draw(too[i],3-c);    
    15 }
    16 
    17 int main() {
    18   scanf("%d",&n);  for (int i=1  ; i<=n; i++) scanf("%d",&p[i]);
    19   Min[n] =  p[n];  for (int i=n-1; i>=1; i--) Min[i]=std::min(Min[i+1],p[i]);
    20   pai[1][0]=1002;  col[n+1]=1;  pai[2][0]=1002;  p[n+1]=1001;
    21   for (int i=1  ; i<=n-2; i++)
    22   for (int j=i+1; j<=n-1; j++) 
    23   if(p[i]<p[j]&&Min[j+1]<p[i]) line(i,j);
    24   for (int i=1  ; i<=n  ; i++) 
    25    if ( !col[ i ] )  draw(i,1);
    26   for (int i=1  ; i<=n+1; i++) {
    27     while (1)
    28     if ( pai[1][l[1]] == should ) {should++; l[1]--; printf("b ");} else
    29     if ( pai[2][l[2]] == should ) {should++; l[2]--; printf("d ");} else break;
    30     pai[ col[i] ][ ++l[ col[i] ] ]=p[i];
    31     if (i<n+1) printf("%c ", char(2*col[i]+95) );
    32   }
    33 }
    无注释33行代码~

          II、有注释有节操的代码能把33行的代码加注释加成53行也算一种技能吧= =!

          
     1 #include<cstdio> 
     2 #include<iostream>   
     3 #include<algorithm> 
     4 using namespace std;
     5 
     6 int p [1010],col[1010],Min[1010],pai[3][1010],l[3]; //p存进栈顺序  ,col为颜色,Min下文有说;
     7                                                      //pai为两个单栈,l 为两个单栈内的个点数;
     8                                                      
     9 int wei[10010],las[10010],too[10010];               //哈希表:wei:点在表中最后记录位置;
    10                                                      //        las:某位置在表中上一记录位置;
    11                                                      //        too:存冲突对象;
    12                                                      
    13 int n,should=1;                                      //should为该出栈的点;
    14 
    15 void line(int x,int y) {                             //将冲突点连起来
    16   las[++t] = wei[x]; wei[x] = t; too[t] = y;         //记录上一记录位置,再更新位置,存取冲突点
    17   las[++t] = wei[y]; wei[y] = t; too[t] = x;
    18 }
    19 
    20 void draw(int x,int c) {                             //将x 染成颜色 c(1~2)
    21   if (!col[x]   )  col[x]=c;             else
    22   if ( col[x]!=c) {printf("0");exit(0);} else return;//如果x 有颜色且不为 c那么就无解
    23   for (int i=wei[x]; i; i=las[i]) draw(too[i],3-c);     //将与它冲突的点,染上另一个颜色
    24 }
    25 
    26 int main() {
    27   scanf("%d",&n);  
    28   for (int i=1  ; i<=n; i++) scanf("%d",&p[i]);
    29   
    30   Min[n] =  p[n];                             //求第 i ~ n 点中最小的点Min[i] 
    31   for (int i=n-1; i>=1; i--) 
    32    Min[i]=min(Min[i+1],p[i]); 
    33                    
    34   pai[1][0]=1002;  col[n+1]=1 ;               //初始化栈顶边界、栈底边界
    35   pai[2][0]=1002;  p[n+1]=1001;               //对最后一位元素之后那位处理以便最后把所有元素压出栈
    36                    
    37   for (int i=1  ; i<=n-2; i++)
    38   for (int j=i+1; j<=n-1; j++) 
    39   if  (p[i]<p[j]&&Min[j+1]<p[i]) line(i,j);   //枚举每对点,如果有冲突,则连线(代表冲突) 
    40   
    41   for (int i=1  ; i<=n  ; i++)                //进行染色,即分成两个客栈,然后同时进行单栈排序
    42   if  ( !col[ i ] )  draw(i,1);
    43   
    44   //科学的打法是进行abcd优先判定的贪心模拟,以下是伤心病况的不科学打法— —!
    45   for (int i=1  ; i<=n+1; i++) {              //模拟单栈排序,两个同时进行,到n+1是为了所有元素出栈
    46     while (1)                                 //能出就出
    47     if ( pai[1][l[1]] == should ) {should++; l[1]--; printf("b ");} else
    48     if ( pai[2][l[2]] == should ) {should++; l[2]--; printf("d ");} else break;
    49     pai[ col[i] ][ ++l[ col[i] ] ]=p[i];      //进栈(就是这行!!!!发现了吗!!!!!!)
    50     if (i<n+1) printf("%c ", char(2*col[i]+95) );
    51   }  
    52 }
    有注释有节操的52行代码~

       六、吐槽:

          有看33行无注释代码的并且有看最后模拟的人应该发现了— —!没错—>_—>下标当时给打错了。。LOOK:

            pai[ col[ ] ][ ++l[ col[ i ] ] ]=p[i];

           i 是代表第 i 个进栈,但p[ i ]代表的才是第 i 个进栈的元素,在染色的时候是给元素进行染色,而不是对该元素的进栈顺序

        进行染色,就是说正确的应该是:

            pai[ col[ p[i] ] ][ ++l[ col[ p[i] ] ] ]=p[i];

          hehe然后喜感的是上一行下标打对的那个是错的— —,因为想想也知道那样模拟出来的答案不一定是最优的,但把元素改成

        与它完全无关的它的进栈顺序的颜色(染色染的根本就不是它啊— —!)就可以喜感的AC了— —!  而且做过随机数据— —!与

        正解对于随机数据的结果目前都一样— —!


                                                                                                                          END

  • 相关阅读:
    <<构建之法>>--第二次作业
    锁的内存语义
    MySQL触发器
    每天一个小示例 opencv(1)颜色直方图的统计 calcHist_Demo.cpp
    通过支持向量排名法来做行人鉴定
    数字信号处理101——DSP系统设计入门课程(1)
    matlab 与c++的混编
    FPGA图像处理之行缓存(linebuffer)的设计一
    基于FPGA的数字识别的实现
    使用matlab生成sine波mif文件
  • 原文地址:https://www.cnblogs.com/qq359084415/p/3386260.html
Copyright © 2011-2022 走看看