描述

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

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

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

输入

第一行是一个由大写字母'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)问题的更多相关文章

  1. 【Tsinghua OJ】灯塔(LightHouse)问题

    描述 海上有许多灯塔,为过路船只照明.从平面上看,海域范围是[1, 10^8] × [1, 10^8] . (图一) 如图一所示,每个灯塔都配有一盏探照灯,照亮其东北.西南两个对顶的直角区域.探照灯的 ...

  2. 【Tsinghua OJ】范围查询(Range)问题

    [问题描述]数轴上有n个点,对于任一闭区间 [a, b],试计算落在其内的点数. [输入]第一行包括两个整数:点的总数n,查询的次数m.第二行包含n个数,为各个点的坐标.以下m行,各包含两个整数:查询 ...

  3. Tsinghua OJ Zuma

    Description Let's play the game Zuma! There are a sequence of beads on a track at the right beginnin ...

  4. 【Tsinghua OJ】多米诺骨牌(domino)问题

    (domino.c/cpp)[问题描述] 小牛牛对多米诺骨牌有很大兴趣,然而她的骨牌比较特别,只有黑色和白色的两种.她觉 得如果存在连续三个骨牌是同一种颜色,那么这个骨牌排列便是不美观的.现在她有n个 ...

  5. 【Tsinghua OJ】循环移位(Cycle)

    Description Cycle shifting refers to following operation on the sting. Moving first letter to the en ...

  6. 【Tsinghua OJ】隧道(Tunel)问题

    描述 现有一条单向单车道隧道,每一辆车从隧道的一端驶入,另一端驶出,不允许超车 该隧道对车辆的高度有一定限制,在任意时刻,管理员希望知道此时隧道中最高车辆的高度是多少 现在请你维护这条隧道的车辆进出记 ...

  7. ACM/ICPC 之 双向链表_构造列表-模拟祖玛 (TSH OJ-Zuma(祖玛))

    这一题是TsingHua OJ上的一道题目,学堂在线的一位数据结构老师的题目(原创),所以我直接把题目先贴下来了,这道题对复习双向链表很有帮助,而且也对数据结构中List,也就是对列表的回顾也是很有帮 ...

  8. ACM/ICPC 之 快排+归并排序-记录顺序对(TSH OJ-LightHouse(灯塔))

    TsingHua OJ 上不能使用<algorithm>头文件,因此需要手写快排(刚开始写的时候自己就出了很多问题....),另外本题需要在给横坐标排序后,需要记录纵坐标的顺序对的数量,因 ...

  9. [LeetCode] Zuma Game 祖玛游戏

    Think about Zuma Game. You have a row of balls on the table, colored red(R), yellow(Y), blue(B), gre ...

随机推荐

  1. HTML5自学笔记[ 11 ]canvas绘图基础1

    html5新增<canvas>标签用于绘制图像,默认宽高是300*150,canvas的宽高需要在这里设置,在css中设置会有问题:设置的形状,如矩形,会等比缩放,而非设置的宽高. 在不支 ...

  2. (29)odoo的可用小图标

    * 系统的小图标都采用了 fontawesome    官网是 http://fontawesome.dashgame.com/    * 运用小图标    # 首先打开官网 http://fonta ...

  3. LayoutInflater和inflate()方法的用法

    LayoutInflater作用是将layout的xml布局文件实例化为View类对象. 实现LayoutInflater的实例化共有3种方法, (1).通过SystemService获得 Layou ...

  4. 《Play for Java》学习笔记(七)数据类型解析——Body parser

    一.什么是body parser? body parser(不知道具体如何翻译,~~~~(>_<)~~~~ )指一个HTTP请求 (如POST和PUT操作)所包含的文本内容(body),这 ...

  5. 修改weblogic部署的应用名称

    通过weblogic管理后台console进行发布本地项目的时候,它会默认以WEB-INF的上一级目录作为访问路径,如,假如你的项目WEB-INF目录的上一层是WebRoot,那么发布后,访问的路径默 ...

  6. 我们无法找到服务器加载工作簿的数据模型"的 SharePoint 网站,当您刷新 Excel 2013 工作簿中的数据透视表时出错

    假定您使用 Analysis Services 源在 Microsoft Excel 2013 中创建数据透视表.将 Excel 工作簿上载到 Microsoft SharePoint 网站中.当您尝 ...

  7. Ubuntu 查看/修改文件编码

    使用enca工具可以查看和修改文件编码 1.安装 sudo apt-get install enca 2.使用 查看文件编码 enca –L zh_CN file_name 修改文件编码 enca – ...

  8. html,body最顶层元素.

    1,元素百比分是相对父元素,所有元素默认父元素是body. absolute,fixed[只有一个父元素,浏览器窗口]除外[浏览器窗口,为父元素].css3:vh,vw也永远相对,浏览器窗口.heig ...

  9. 可滑动的ToggleButton(开关)

    2013-12-28 17:25:01 网上看到一篇关于可滑动的ToogleButton的文章,有代码,觉得挺好,但是不符合我的要求,因此在他的代码基础上改了一些.(作者看到了勿喷啊,实在找不到原文了 ...

  10. APP主流UI框架结构

    uitabbar控制器为根控制器在其基础上 放多个导航栏控制器