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

开发商最近准备为玩家写一个游戏过程的回放工具。他们已经在游戏内完成了过程记录的功能,而回放功能的实现则委托你来完成。
游戏过程的记录中,首先是轨道上初始的珠子序列,然后是玩家接下来所做的一系列操作。你的任务是,在各次操作之后及时计算出新的珠子序列。
输入
第一行是一个由大写字母'A'~'Z'组成的字符串,表示轨道上初始的珠子序列,不同的字母表示不同的颜色。
第二行是一个数字n,表示整个回放过程共有n次操作。
接下来的n行依次对应于各次操作。每次操作由一个数字k和一个大写字母Σ描述,以空格分隔。其中,Σ为新珠子的颜色。若插入前共有m颗珠子,则k ∈ [0, m]表示新珠子嵌入之后(尚未发生消除之前)在轨道上的位序。
输出
输出共n行,依次给出各次操作(及可能随即发生的消除现象)之后轨道上的珠子序列。
如果轨道上已没有珠子,则以“-”表示。
输入样例
ACCBA
5
1 B
0 A
2 B
4 C
0 A
输出样例
ABCCBA
AABCCBA
AABBCCBA
-
A
限制
0 ≤ n ≤ 10^4
0 ≤ 初始珠子数量 ≤ 10^4
时间:2s,内存:256MB
提示
列表
【Solution】
先贴源码:
#include <stdio.h>
#include "string.h"
#include <stdlib.h> typedef char ElemType;
typedef struct node
{
ElemType data;
struct node *next;
struct node *front;
}List, *pList; pList pHead = (pList)malloc(sizeof(List));
pList pTail = (pList)malloc(sizeof(List)); void creat(char *a, int n)
{
int i;
pList pt = pHead; pTail->front = pHead;
pTail->next = NULL;
pHead->next = pTail;
pHead->front = NULL;
pHead->data = pTail->data = '-'; for (i = ; i < n; i++)
{
pList pNew = (pList)malloc(sizeof(List));
pNew->data = a[i];
pNew->front = pt;
pNew->next = pt->next;
pt->next->front = pNew;
pt->next = pNew;
pt = pNew;
}
} void insert(int m, char ch)
{
int i = -;
pList pt = pHead, pNew = (pList)malloc(sizeof(List)); while (i++ < m) pt = pt->next; pNew->data = ch;
pNew->next = pt;
pNew->front = pt->front;
pt->front->next = pNew;
pt->front = pNew;
} void del(int m)
{
pList p1 = NULL, p2 = NULL, p3 = NULL, p4 = NULL, pt = pHead;
pList begin = pHead, end = pTail;
bool boo = true;
int repeat, i = -; // find position
while (i++ < m - ) pt = pt->next; //init for 'begin' and 'end'
begin = pt; end = pt; i = ;
while (i++ < && end->next != pTail) end = end->next; while (boo && pt != pTail)
{
boo = false; repeat = ;
while (pt != end)
{
pt = pt->next; if (pt->front->data == pt->data) repeat++;
else repeat = ; if (repeat == )
{
boo = true;
if (pt->data == pt->next->data)
{
repeat++;
pt = pt->next;
} if (repeat == )
{
p3 = pt; p2 = p3->front; p1 = p2->front;
p1->front->next = p3->next;
p3->next->front = p1->front;
pt = pt->next;
delete p1; delete p2; delete p3;
}
else
{
p4 = pt; p3 = p4->front; p2 = p3->front; p1 = p2->front;
p1->front->next = p4->next;
p4->next->front = p1->front;
pt = pt->next;
delete p1; delete p2; delete p3; delete p4;
} break;
}
} if (boo && pt != pTail)
{
begin = pt; i = ;
while (i++ < && begin->front != pHead) begin = begin->front;
end = pt; i = ;
if (i++ < && end->next != pTail) end = end->next;
pt = begin;
}
}
} void show()
{
pList pt = pHead->next; if (pt == pTail) printf("-");
else
{
while (pt->next != NULL)
{
printf("%c", pt->data);
pt = pt->next;
}
} printf("\n");
} int main(void)
{
char a[];
int n, k;
pList pHead = NULL; gets(a);
scanf("%d\n", &n); creat(a, strlen(a)); for (k = ; k < n; k++)
{
int m;
char ch; scanf("%d ", &m);
do
{
ch = getchar();
} while (!((ch >= 'A') && (ch <= 'Z'))); // insert ch
insert(m, ch); // delete all 3-same block, making it the right string
del(m); // print the string
show();
} return ;
}
可以过 Tsinghua OJ 95%的数据,最后一个点超时,听说要把缓存区调大或者用fread读取数据才能过,暂时无解。
这一类题属于模拟题,也就是按照题意一步一步模拟操作即可,对现实事物的合理抽象以及模拟操作的效率是解决问题的关键。
要注意的几个点:
1、注意列表与向量数据结构的差别。向量可以直接 “循秩访问(call-by-rank)”,所以对于查找操作是O(1)的,插入和删除都是O(n)的,对于二分查找等这样十分依赖于“秩”的算法很重要;而列表是“循位置访问(call-by-position)”,所以对于 插入、删除都是O(1)的操作,而查找操作是O(n)的。要充分注意它们的特点。实际上,对于这道题,虽然提示里写了“列表”,由于每次都要遍历输出和查找,已经是O(n)的了,不见得比用向量做会快多少。
2、列表处理的一个特别好的小技巧:在列表的前面和后面各放置一个哨兵,如果把列表比作一条“绳子”,那么就相当于两头各放置一个手抓的地方,这样无论列表内部会有哪些动态操作(即改变本身结构的操作,比如插入删除,而相应的查找等不改变自己结构的操作则称为静态操作),都可以从一而终地从两头把列表给“拉”出来。好处在于,有很多需要考虑边界情况的问题可以自动化的化为一般化的处理,也不必因为可能的删除或插入操作不断更新列表头。之前做这道题并未这样考虑的结果就是代码里面各种判断是否为NULL以防止边界情况出错,有了哨兵这样的判断很多时候可以一般化处理,判断大大减少。同时也防止了越界错误的发生。
3、像Python那样,总是从一而终地考虑[a, b)这样的左闭右开区间是很有必要的,即区间左界桩总是被问题范围所包含,而右界桩在当前状态则不被包含。它可以大大的减少你思考问题的复杂度。遵循统一的标准也减少了犯错的可能。
4、同样,对于列表所对应的具体的数据结构链表,总是要尤其注意边界情况。要注意的是:抽象数据类型是对数据结构更高层次的抽象。它是一种抽象定义,表现为逻辑上的特征和一些基本操作及语义,并不涉及数据的具体存储方式。比如向量和列表。最常见的对应于这两种抽象数据类型的具体数据类型也就是 数组 和 链表了。抽象有利于定义统一的借口和规范以便更一般化的归纳、使用和处理。
5、对于这道题,自己的一点小优化:
考虑到每次需要删除的部分一定包含插入点,所以每次删除的时候就直接定位到插入点以及它附近。
假设 插入点是k,第一次则考察k-2~k+2这五个点,
假设 有删除操作,设删除区段的后继元素为m,
之后考察 m-2 ~ m+1这四个点。
重复以上两步直到扫描这个区间不再有删除操作。
这样就不需要每次都扫描整个列表来判断需不需要删除了。
这一切都基于,列表的“局部切除手术”只可能发生在插入点附近,并且一定包含插入点。
6、另外:判等否操作比判大小关系操作效率要高;
【AC版代码】
对于这道题,由于每次都要输出 n 次,每次输出都要遍历一遍列表,每次都要调用I/O口,把输出内容压到缓存区,然后打印出来,这样其实消耗了大量的时间。
最后,我考虑不要每操作一次就输出一次,把几次操作的内容存到一个字符串,到达一定的上限再输出,然后AC了。
改进后的源代码:
#include <stdio.h>
#include "string.h"
#include <stdlib.h> #define Len 200000000
#define Up (Len*3/4) typedef char ElemType;
typedef struct node
{
ElemType data;
struct node *next;
struct node *front;
}List, *pList; pList pHead = (pList)malloc(sizeof(List));
pList pTail = (pList)malloc(sizeof(List)); char ans[Len + ];
int forprt = ; void creat(char *a, int n)
{
int i;
pList pt = pHead; pTail->front = pHead;
pTail->next = NULL;
pHead->next = pTail;
pHead->front = NULL;
pHead->data = pTail->data = '-'; for (i = ; i < n; i++)
{
pList pNew = (pList)malloc(sizeof(List));
pNew->data = a[i];
pNew->front = pt;
pNew->next = pt->next;
pt->next->front = pNew;
pt->next = pNew;
pt = pNew;
}
} void insert(int m, char ch)
{
int i = -;
pList pt = pHead, pNew = (pList)malloc(sizeof(List)); while (i++ < m) pt = pt->next; pNew->data = ch;
pNew->next = pt;
pNew->front = pt->front;
pt->front->next = pNew;
pt->front = pNew;
} void del(int m)
{
pList p1 = NULL, p2 = NULL, p3 = NULL, p4 = NULL, pt = pHead;
pList begin = pHead, end = pTail;
bool boo = true;
int repeat, i = -; // find position
while (i++ < m - ) pt = pt->next; //init for 'begin' and 'end'
begin = pt; end = pt; i = ;
while (i++ < && end->next != pTail) end = end->next; while (boo && pt != pTail)
{
boo = false; repeat = ;
while (pt != end)
{
pt = pt->next; if (pt->front->data == pt->data) repeat++;
else repeat = ; if (repeat == )
{
boo = true;
if (pt->data == pt->next->data)
{
repeat++;
pt = pt->next;
} if (repeat == )
{
p3 = pt; p2 = p3->front; p1 = p2->front;
p1->front->next = p3->next;
p3->next->front = p1->front;
pt = pt->next;
delete p1; delete p2; delete p3;
}
else
{
p4 = pt; p3 = p4->front; p2 = p3->front; p1 = p2->front;
p1->front->next = p4->next;
p4->next->front = p1->front;
pt = pt->next;
delete p1; delete p2; delete p3; delete p4;
} break;
}
} if (boo && pt != pTail)
{
begin = pt; i = ;
while (i++ < && begin->front != pHead) begin = begin->front;
end = pt; i = ;
if (i++ < && end->next != pTail) end = end->next;
pt = begin;
}
}
} void show(bool boo)
{
pList pt = pHead->next; if (pt == pTail) ans[forprt++] = '-';
else
{
while (pt->next != NULL)
{
ans[forprt++] = pt->data;
pt = pt->next;
}
} ans[forprt++] = '\n'; if (forprt >= Up || boo)
{
ans[forprt] = '\0';
printf("%s", ans);
forprt = ;
}
} int main(void)
{
char a[];
int n, k;
pList pHead = NULL; gets(a);
scanf("%d\n", &n); creat(a, strlen(a)); for (k = ; k < n; k++)
{
int m;
char ch; scanf("%d ", &m);
do
{
ch = getchar();
} while (!((ch >= 'A') && (ch <= 'Z'))); // insert ch
insert(m, ch); // delete all 3-same block, making it the right string
del(m); // print the string
show(k == n - ? true : false);
} return ;
}
需要注意的点:
1、注意常量 Len 和 Up 的关系,一定不能把 Up 简单地设置为 Len。因为如果那样,可能某次输出前的最后一个操作后的字符串加在原来待输出的大字符串后面,还来不及判断是否超过上限就已经数组下标越界了。
2、Len 已经不能再大了,再大超空间了。
【Tsinghua OJ】祖玛(Zuma)问题的更多相关文章
- 【Tsinghua OJ】灯塔(LightHouse)问题
描述 海上有许多灯塔,为过路船只照明.从平面上看,海域范围是[1, 10^8] × [1, 10^8] . (图一) 如图一所示,每个灯塔都配有一盏探照灯,照亮其东北.西南两个对顶的直角区域.探照灯的 ...
- 【Tsinghua OJ】范围查询(Range)问题
[问题描述]数轴上有n个点,对于任一闭区间 [a, b],试计算落在其内的点数. [输入]第一行包括两个整数:点的总数n,查询的次数m.第二行包含n个数,为各个点的坐标.以下m行,各包含两个整数:查询 ...
- Tsinghua OJ Zuma
Description Let's play the game Zuma! There are a sequence of beads on a track at the right beginnin ...
- 【Tsinghua OJ】多米诺骨牌(domino)问题
(domino.c/cpp)[问题描述] 小牛牛对多米诺骨牌有很大兴趣,然而她的骨牌比较特别,只有黑色和白色的两种.她觉 得如果存在连续三个骨牌是同一种颜色,那么这个骨牌排列便是不美观的.现在她有n个 ...
- 【Tsinghua OJ】循环移位(Cycle)
Description Cycle shifting refers to following operation on the sting. Moving first letter to the en ...
- 【Tsinghua OJ】隧道(Tunel)问题
描述 现有一条单向单车道隧道,每一辆车从隧道的一端驶入,另一端驶出,不允许超车 该隧道对车辆的高度有一定限制,在任意时刻,管理员希望知道此时隧道中最高车辆的高度是多少 现在请你维护这条隧道的车辆进出记 ...
- ACM/ICPC 之 双向链表_构造列表-模拟祖玛 (TSH OJ-Zuma(祖玛))
这一题是TsingHua OJ上的一道题目,学堂在线的一位数据结构老师的题目(原创),所以我直接把题目先贴下来了,这道题对复习双向链表很有帮助,而且也对数据结构中List,也就是对列表的回顾也是很有帮 ...
- ACM/ICPC 之 快排+归并排序-记录顺序对(TSH OJ-LightHouse(灯塔))
TsingHua OJ 上不能使用<algorithm>头文件,因此需要手写快排(刚开始写的时候自己就出了很多问题....),另外本题需要在给横坐标排序后,需要记录纵坐标的顺序对的数量,因 ...
- [LeetCode] Zuma Game 祖玛游戏
Think about Zuma Game. You have a row of balls on the table, colored red(R), yellow(Y), blue(B), gre ...
随机推荐
- meta viewport 详解
ViewPort <meta>标记用于指定用户是否可以缩放Web页面,如果可以,那么缩放到的最大和最小缩放比例是什么.使用ViewPort <meta>标记还表示文档针对移动设 ...
- Qt之模拟时钟
简述 Qt自带的示例中有一个是关于时钟,演示了如何用QPainter的转换和缩放特性来绘制自定义部件. 其中主要包含了时针.分针的绘制,并不包含秒针.下面,我们在原示例的基础上进行扩展. 简述 实现方 ...
- hdu 3074 Zjnu Stadium (带权并查集)
Zjnu Stadium Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Tota ...
- Java中Array的常用方法
0.创建/声明一个数组 1 2 3 String[] aArray = new String[5]; String[] bArray = {"a","b",&q ...
- override和overload的区别
override(重写,覆盖) 1.方法名.参数.返回值相同. 2.子类方法不能缩小父类方法的访问权限. 3.子类方法不能抛出比父类方法更多的异常(但子类方法可以不抛出异常). 4.存在于父类和子类之 ...
- [转载]SAP BASIS学习手册
原文地址:SAP BASIS学习手册作者:sapren 1:)要用scc4定义一个新的client,同时定义好类型(T,P,D等) 2:)用user/pasword: (sap*/pass) ...
- 课堂练习&课下作业
设计思路: 列举出买十本的所有情况:1.一本的时候不打折扣 2.两本的时候买两本价最低 3.三本的时候买三本价最低 4.四本的时候买四本价最低 5.五本的时候买五本价最低 6.六本的时候分一本和五本价 ...
- 陈朱兴-js写法【案例】:
ajax请求: 一.从服务器端请求数据: var url = '';url = 'https://api.weixin.qq.com/sns/oauth2/access_token?appid='+ ...
- ext DateTime.js在ie下显示不全
问题: ext在使用DateTime.js的时候会出现在日期控件在ie下显示不完成.如图 少了底部的“今天按钮”. 解决方法: 在ext/ux/form/DateTime.js (我的是这个路径,根 ...
- bind,unbing,on,live,delegate绑定和解绑事件
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN""http://www.w3.org/T ...