THU-OJ-"PA1-2"-"Zuma Issue"

来源:互联网 发布:mysql,书 编辑:程序博客网 时间:2024/05/17 09:31

题目描述
祖玛是一款曾经风靡全球的游戏,其玩法是:在一条轨道上初始排列着若干个彩色珠子,其中任意三个相邻的珠子不会完全同色。此后,你可以发射珠子到轨道上并加入原有序列中。一旦有三个或更多同色的珠子变成相邻,它们就会立即消失。这类消除现象可能会连锁式发生,其间你将暂时不能发射珠子。

开发商最近准备为玩家写一个游戏过程的回放工具。他们已经在游戏内完成了过程记录的功能,而回放功能的实现则委托你来完成。

游戏过程的记录中,首先是轨道上初始的珠子序列,然后是玩家接下来所做的一系列操作。你的任务是,在各次操作之后及时计算出新的珠子序列。

输入
第一行是一个由大写字母’A’~’Z’组成的字符串,表示轨道上初始的珠子序列,不同的字母表示不同的颜色。

第二行是一个数字n,表示整个回放过程共有n次操作。

接下来的n行依次对应于各次操作。每次操作由一个数字k和一个大写字母Σ描述,以空格分隔。其中,Σ为新珠子的颜色。若插入前共有m颗珠子,则k ∈ [0, m]表示新珠子嵌入之后(尚未发生消除之前)在轨道上的位序。

输出
输出共n行,依次给出各次操作(及可能随即发生的消除现象)之后轨道上的珠子序列。

如果轨道上已没有珠子,则以“-”表示。

样例
见英文题面

限制
0 ≤ n ≤ 10^4

0 ≤ 初始珠子数量 ≤ 10^4

时间:2 sec

内存:256 MB

提示
列表

思考:
这道题在二分查找题目之后,根据教学顺序应该使用列表。而提示中也表示使用列表来做。因此考虑列表实现。
由于OJ上并没有库,因此只能够自己定义列表类。不过这样也很好,正好能够定制一些独特的功能,也有助于对列表的理解。

代码如下:
#include <stdio.h>#include <string.h>#include <cstring>#include <stdlib.h>#define Posi(T) ListNode<T>*#define Rank int#define PRE 1         //向前搜索#define AFT 2         //向后搜索using namespace std;template <typename T> struct ListNode{//标准节点类    T data;    Posi(T) pred;    Posi(T) succ;    ListNode(){};    ListNode(T e,Posi(T) p=NULL,Posi(T) s=NULL):data(e),pred(p),succ(s){};    Posi(T) insertAsPred(T const& e){        Posi(T) x=new ListNode(e,pred,this);        pred->succ=x;        pred=x;        return x;    };    Posi(T) insertAsSucc(T const& e){        Posi(T) x=new ListNode(e,this,succ);        succ->pred=x;        succ=x;        return x;    };};//快速IOconst int SZ = 1 << 20;  //提升IO buffstruct fastio{    char inbuf[SZ];    char outbuf[SZ];    fastio(){        setvbuf(stdin, inbuf, _IOFBF, SZ);        setvbuf(stdout, outbuf, _IOFBF, SZ);    }}io;template <typename T> class List{//列表类    private:        int _size;        Posi(T) header;        Posi(T) trailer;    public:        void init(){            header =new ListNode<T>;            trailer=new ListNode<T>;            header->succ=trailer;            header->pred=NULL;            trailer->pred=header;            trailer->succ=NULL;            _size=0;        }        Posi(T) operator[](Rank r) const{//寻秩访问            Posi(T) p=header;            while(-1<r--) p=p->succ;            return p;        }        //获取头尾节点        Posi(T) HEADER()const{            return header;        }        Posi(T) TRAILER()const{            return trailer;        }        //输出列表内容        void out()const{            Posi(T) p=header;            if(p->succ==trailer){                printf("-");            }            else{                while((p=p->succ)!=trailer){                    printf("%c",p->data);                }            }            printf("\n");        }        //直接连接某两个节点        void link(Posi(T) p1,Posi(T) p2){            p1->succ=p2;            p2->pred=p1;        }};void elim(Posi(char) p,List<char> e); //以节点p为中心,对e序列进行对消操作int main(){    //************************************************************    //Data Input        char str[10001];        int times;        gets(str);        int length=strlen(str);        List<char> zuma;        zuma.init();        Posi(char) p=zuma.HEADER();        for(int i=0;i<length;i++){            Posi(char) e=p->insertAsSucc(str[i]);            p=e;        }        scanf("%d",&times);        int* posi=new int[times+1];        char* word=new char[times+1];        for(int i=0;i<times;i++){            scanf("%d %s",&posi[i],&word[i]);        }    //*************************************************************    //Process        for(int i=0;i<times;i++){            Posi(char) insert=zuma[posi[i]-1]->insertAsSucc(word[i]);            bool elim_flag=true;            while(elim_flag){                    insert=elim(insert,zuma);                    if(insert==zuma.TRAILER()){                        elim_flag=false;                    }            }            zuma.out();        }}//前后同字符数量搜索int pre_elim(Posi(char) p,List<char> e){    int k=0;    char t=p->data;    while((p->pred!=e.HEADER())&&(p->pred->data==t)){        p=p->pred;        k++;    }    return k;}int aft_elim(Posi(char) p,List<char> e){    int k=0;    char t=p->data;    while((p->succ!=e.TRAILER())&&(p->succ->data==t)){        p=p->succ;        k++;    }    return k;}//连接字符Posi(char) dellink(Posi(char) p,List<char> e,int pre,int aft){    Posi(char) p1=p;    Posi(char) p2=p;    while(pre-->-1){        p1=p1->pred;    }    while(aft-->-1){        p2=p2->succ;    }    e.link(p1,p2);    return  p2;}//对消函数Posi(char) elim(Posi(char) p,List<char> e){    int pre,aft;    pre=pre_elim(p,e);    aft=aft_elim(p,e);    if(pre+aft>=2){         return dellink(p,e,pre,aft);    }    else{        return e.TRAILER();    }}

以上即为利用列表解决zuma问题。在OJ上测例通过率为95%,情况如下图所示:
这里写图片描述
在编程中遇到并解决了下列问题:

空格字符输入问题
OJ测例3是空字符的输入。因为最初不了解getchar()、scanf()、gets()之间的区别,使用了scanf输入字符。以下是三者的区别

getchar(),可以读取空格回车在内的一个字符,常常用来处理上文输入时候缓冲区内的回车等,以确保下文可以正常接收
scanf(),接收一个字符串,以空格回车作为结束标识,如果输入字符串那么回车会变成’\0’被放在字符串结尾,如果是其他类型,回车会继续停留在缓冲区等待下个变量接受
gets(),接受一个长串,期中可以接受空格等,以回车符作为结束标志,但其也可以接受一个回车,产生一个空串.如果上句是scanf并且输入的是字符串,那么不会接收回车

快速IO
OJ上测试的数据量是非常大的,所以在设计程序时必须考虑时间复杂度。为了实现快速输入输出,需要调大流缓冲区。这也算是OJ题目必备的奇淫巧技吧。
FASTIO代码:

快速IO
const int SZ = 1<<20; struct fastio{ //fast io
char inbuf[SZ];
char outbuf[SZ];
fastio(){
setvbuf(stdin,inbuf,_IOFBF,SZ);
setvbuf(stdout,outbuf,_IOFBF,SZ);
解释:设置文件缓冲区函数
      void setbuf(FILE *stream,char *buf);
      void setvbuf(FILE *stream,char *buf,int type,unsigned size);
  这两个函数将使得打开文件后,用户可建立自己的文件缓冲区,而不使用fopen()函数打开文件设定的默认缓冲区。
  对于setbuf()函数,buf指出缓冲区长度,由stdio.h中定义的宏BUFSIZE的值决定,缺省为512字节。当buf为空时,setbuf函数将使的文件I/O不带缓冲。
  对setvbuf函数,则由malloc函数来分配缓冲区,参数size指明了缓冲区的长度。
  type则表示了缓冲的类型,其值可以取如下值:
  _IOFBF 文件全部缓冲,即缓冲区装满后,才能对文件读写
  _IOLBF 文件行缓冲,即缓冲区接收到一个换行符时,才能对文件读写

Runtime error(ExitCode 6)问题
Linux上Runtime error(ExitCode 6)错误,与动态内存的分配与销毁等有关。但是网上一直没有能够找到一个权威的解释。最初在测例17、19上出现该错误,并且久久无法解决。直到将代码中每一个数组进行debug,最后才锁定了问题:
Main函数中:

        int* posi=new int[times+1];        char* word=new char[times+1];        for(int i=0;i<times;i++){            scanf("%d %s",&posi[i],&word[i]);        }

尽管scanf只会用到posi[time-1],但是申请内存时却要申请int[time+1]大小的内存,否则就要报错。理论上来讲,这件事是说不通的。但是由于OJ不能gdb调试,看不见内存的情况,所以暂时也无法进一步细细讨论了~希望有大腿能够指教!

1 0
原创粉丝点击