THU数据结构编程作业一:祖玛(Zuma)
来源:互联网 发布:逍遥模拟器优化cpu 编辑:程序博客网 时间:2024/06/06 18:40
* 祖玛(Zuma) *
描述
祖玛是一款曾经风靡全球的游戏,其玩法是:在一条轨道上初始排列着若干个彩色珠子,其中任意三个相邻的珠子不会完全同色。此后,你可以发射珠子到轨道上并加入原有序列中。一旦有三个或更多同色的珠子变成相邻,它们就会立即消失。这类消除现象可能会连锁式发生,其间你将暂时不能发射珠子。
开发商最近准备为玩家写一个游戏过程的回放工具。他们已经在游戏内完成了过程记录的功能,而回放功能的实现则委托你来完成。
游戏过程的记录中,首先是轨道上初始的珠子序列,然后是玩家接下来所做的一系列操作。你的任务是,在各次操作之后及时计算出新的珠子序列。
输入
第一行是一个由大写字母’A’~’Z’组成的字符串,表示轨道上初始的珠子序列,不同的字母表示不同的颜色。
第二行是一个数字n,表示整个回放过程共有n次操作。
接下来的n行依次对应于各次操作。每次操作由一个数字k和一个大写字母Σ描述,以空格分隔。其中,Σ为新珠子的颜色。若插入前共有m颗珠子,则k ∈ [0, m]表示新珠子嵌入之后(尚未发生消除之前)在轨道上的位序。
输出
输出共n行,依次给出各次操作(及可能随即发生的消除现象)之后轨道上的珠子序列。
如果轨道上已没有珠子,则以“-”表示。
样例
Input
ACCBA51 B0 A2 B4 C0 A
Output
ABCCBAAABCCBAAABBCCBA-A
限制
0 ≤ n ≤ 10^4
0 ≤ 初始珠子数量 ≤ 10^4
时间:2 sec
内存:256 MB
提示
列表
先上整个程序:
#include<iostream>#include<list>#include<cstdio>#define MAXSIZE 20000using namespace std;const int SZ = 1<<20; //fast io struct fastio{ char inbuf[SZ]; char outbuf[SZ]; fastio(){ setvbuf(stdin,inbuf,_IOFBF,SZ); setvbuf(stdout,outbuf,_IOFBF,SZ); } }io; int main(){ #ifndef _OJ_ freopen("input.txt", "r", stdin); freopen("output.txt", "w", stdout); #endif char s[MAXSIZE]; char c; int sz(0);//size of string scanf("%c", &c); while(c != '\n'){ s[sz++] = c; scanf("%c",&c); } s[sz++] = '\0'; int Times; scanf("%d", &Times); int (*Insert)[2] = new int[Times][2](); for(int i = 0; i < Times; ++i) scanf("%d %c", &Insert[i][0], &Insert[i][1]); for(int i = 0, j = 0; i < Times; ++i){ for( j = sz - 1; j >= Insert[i][0]; --j) s[j+1] = s[j]; s[j+1] = Insert[i][1]; ++sz; /*******************************/ int Location = Insert[i][0];//insert site int p1= Location, p2 = Location, fix = Location; int P1 = Location, P2 = Location; int rep(1);//times of repetition bool p1Stop = false, p2Stop = false; while(!p1Stop || !p2Stop){ //move p1 while(!p1Stop){ if(p1 > 0 && s[--p1] == s[fix]) ++rep; else p1Stop = true; } //move p2 while(!p2Stop){ if(p2 < sz - 1 && s[++p2] == s[fix]) ++rep; else p2Stop = true; } if(rep >= 3){ if(s[p1] == s[fix]) P1 = p1; else P1 = p1 + 1; if(s[p2] == s[fix] ) P2 = p2; else P2 = p2 -1; } //reset rep if(rep >=3 && s[p1] == s[p2]){ rep = 2; fix = p1; p1Stop = false; p2Stop = false; } } if(P2 - P1 >= 2){ for(int i = P1, j= P2; j < sz - 1 ; ++i, ++j) s[i] = s[j + 1]; sz -= (P2-P1+1); } /*******************************/ /* bool Flag = true; if(sz >= 4){ while(Flag){ Flag = false; for(int i = 0; i < sz - 1; ++i){ int tt = 1;//counter to indicate the times of repetition for(int j = i + 1; i < sz; ++j){ if(s[j] == s[i]) ++tt; else break; } if(tt >= 3){ Flag = true; for(int k = i; k < sz-tt; ++k) s[k] = s[k + tt]; sz -= tt; } } } }*/ if(sz <= 1) printf("-\n"); else printf("%s\n", s); } delete[] Insert; return 0;}
以下对程序的部分内容进行解释:
1、 OJ上测试的数据量是非常大的,所以在设计程序时必须考虑时间复杂度。首先为了实现快速输入输出,调大流缓冲区。
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); } }io;
开始在OJ上测试时候总是只能通过95%,对于最后一组数据总是超时,而且在程序算法改进了以后(稍后会进行说明),依然超时,改进后的算法的时间复杂度应该是极大地降低了的。因为以前在自己的 linux 虚拟机上测试过数据量较大的数据总是出错,我就考虑到是否是和输入输出缓存有关系,毕竟自己只是刚入门的菜鸟,对缓存并没有足够的了解。在网上搜了很多,都提到了扩大缓冲区但是基本都没有解决方法。直到看到这位大神的代码,才恍然大悟:http://blog.csdn.net/baidu_23318869/article/details/41284075 。后来测试不仅最后一组数据通过而且速度也很快。我将这段代码加到前一个“范围查询”的程序中,速度果然加快了很多,膜拜大神~~~
2、虽然题目提示用列表解决,因为链表的插入和删除一个节点的时间复杂度为 O(1) ,但是我并没有使用列表,下次我想用列表重新实现一次。因为担心THU的OJ中没有c++的string头文件(上一篇的结尾已经解释),我用的是字符数组存储字符序列。注意为了方便输出我在数组的结尾插入了‘\0’,这样就可以以C字符串输出。关于需要插入的字符和插入的位置,我用了一个二维数组来存储,如下
int (*Insert)[2] = new int[Times][2](); for(int i = 0; i < Times; ++i) scanf("%d %c", &Insert[i][0], &Insert[i][1]);
虽然整个数组为整形,但是我通过 scanf 将数组的第二列调整输入为 char 型,这样就可以保存想要保存的数据,并能够方便获取。
3、程序的主题部分是个大的 for 循环。对于每一个有待插入的字符,首先进行插入 :
for( j = sz - 1; j >= Insert[i][0]; --j) s[j+1] = s[j]; s[j+1] = Insert[i][1]; ++sz;
后面就是对于连续出现的字符序列进行删除了。
起初,我使用的呗注释掉的那部分方法。通过对插入后的序列进行逐论扫描,每次扫描都删除一个连续字符序列,这种方法很笨但是确实很好实现。
bool Flag = true; if(sz >= 4){ while(Flag){ Flag = false; for(int i = 0; i < sz - 1; ++i){ int tt = 1;//counter to indicate the times of repetition for(int j = i + 1; i < sz; ++j){ if(s[j] == s[i]) ++tt; else break; } if(tt >= 3){ Flag = true; for(int k = i; k < sz-tt; ++k) s[k] = s[k + tt]; sz -= tt; } } } }
在OJ上测试后最后一组数据超时无法通过,我以为是因为自己的程序方法太笨复杂度较高引起的超时,其实主要的原因应该是前面提到的流缓存不够大的原因。但是以上错误的认为,让我不得不重新考虑算法的实现。
4、重新思考算法的实现。上面的方法在字符删除上耗时应该是最多了,每次都要对字符序列进行扫描,而且每次扫描只能删除一组重复序列,效率是很低的。于是,我希望找到一种方法提前知道哪些元素会被删除,最后一次性删除所有数据,这样就只需要一次删除操作。
想到了用指针的方法(我这里用的是数组的下标)进行标记。首先设置两个标志都指向插入点的字符,然后将两个标志分别向左和向右移动,记录字符,当左右指针对于同一个字符记录的次数大于等于3 的时候就说明两个指针之间的字符串是需要删除的。
整个过程如下图:
红色的 C 是插入的字符,Rep用来记录重复次数。
···首先指针p1和p2都指向插入位置,然后分别向左和向右移动直到遇到不同的字符就停止;
···如果当两个指针都停止时候的Rep的值大于等于3,则确定p1和p2之间的字符是需要删除的。
···此时,如果p1 和 p2 所指向的字符相同,则说明有可能后续的字符还需要删除,于是将Rep置为2,并再次移动p1和p2,同时记录Rep,如此反复。
···直到p1 和p2指向不同的字符或者已经移动到字符串两端。
整个过程也并不复杂,首先的难点是如果p1 和 p2 向两边移动停止时Rep小于3时,如何确定之前记录的需要删除的字符串的位置,我这里增加了两个变量P1 和P2来记录前一次确定需要删除的字符串的位置。其次,当其中一个指针移动到字符串的一端时,端点的字符串可能需要删除也可能不需要删除,这是比较难确定的。这里我增加了一个判断来解决这个问题:
if(rep >= 3){ if(s[p1] == s[fix]) P1 = p1; else P1 = p1 + 1; if(s[p2] == s[fix] ) P2 = p2; else P2 = p2 -1; }
被这两个问题折磨的不要不要的~~~~ ヾ(≧O≦)〃嗷~
最后测试,发现运行明显快了不少。
另:写到这里,发现其实开始的插入操作也可以没有的,直接记录要插入的位置和字符,然后只需要做删除操作不就好了!发现自己智商又捉急了~ヾ(≧O≦)〃嗷~ヾ(≧O≦)〃嗷~ヾ(≧O≦)〃嗷~
- THU数据结构编程作业一:祖玛(Zuma)
- THU数据结构编程作业一:查询范围(Range)
- THU数据结构编程作业一:列车调度(Train)
- THU数据结构编程作业一:隧道(Tunel)
- Tsinghua OJ 数据结构编程作业:祖玛(Zuma)
- THU数据结构编程作业一:真二叉树重构(Proper Rebuild)
- THU-OJ-"PA1-2"-"Zuma Issue"
- 编程作业(一)
- 祖玛(Zuma)
- 数据结构1-2Zuma
- Tsinghua MOOC 祖玛(Zuma)
- Tsinghua OJ:祖玛(Zuma)
- THU组合数学第二周作业
- Zuma
- [数据结构]第五次作业:huffman编码及其译码(一)
- 校园导航——数据结构作业(一)
- 机房练习赛hao 【Tsinghua OJ】祖玛(Zuma) (字符串)
- 第一次作业(数据结构)
- Linux 应用程序编程基础
- 图解HTTP协议 第4章 返回结果的HTTP状态码学习笔记
- PHP学习笔记(七):命名空间
- C++基础复习---1(函数指针,指针函数)
- USB host 通信
- THU数据结构编程作业一:祖玛(Zuma)
- 图解HTTP协议 第5章 与HTTP协作的Web服务器学习笔记
- Android方法数不能超过65K的解决方案
- POJ1860 Currency Exchange 最短路bellman-ford
- poj 2187
- caffe实现RNN(recursive Neural Network, recursive NN)
- 敢创业但不敢上台演讲?
- 如何寻找连通域的重心
- Factorial Trailing Zeroes