本周我进行了针对模拟题的训练,在比赛中,模拟题是考察一个人代码综合能力的最佳体现,也是考验选手对细节的把握能力的有力工具。本周的模拟题大多来自WF和Regional,而前者的模拟题一般细节比较多,编程比较复杂。下面来根据我本周的训练情况来简要总结一下这方面需要注意的地方。
首先,应该完全理解题意,这一步相当关键,如果连题意都理解错了,那么后面无论花多大功夫去调试代码都是无济于事,因为你一直在实现一个错误的模拟过程。弄明白题目的全部流程,接下来就是考虑顶层设计:有哪些全局变量,需要设计哪些结构体,需要计算什么参数,需要用到什么函数,程序的大致框架应该是什么样要想清楚。比如这次基础巩固(1)中的A题,要求在棋盘上移动空格,并打印出最后的棋盘状态,因此要设计交换函数,打印函数;再比如这次训练中比较复杂的K题,模拟一个医院一天手术室,恢复室的使用状况,应该立即想到用优先队列,由于题目中有很多量都跟病人有关,因此要专门为每个病人设计一个结构体,存放他的姓名,手术室编号,恢复室编号,开始手术时间,手术时长,开始恢复时间,恢复时长;在最后要输出一个标准的24小时制的时间,因此还要为时间编写一个结构体,用于输出时和分。
第二步,仔细审题,注意题目中的细节描述,和一些隐藏的信息。比如这次的E题,要求模拟黑白棋的下棋过程,其中M指令要求“当前玩家”放置棋子,如果该位置不合法,则更换到对方玩家。同时在题目一开始题到一句话:“两人轮流下棋”,这就告诉我们处理M指令时要考虑更换当前玩家,如果不注意这个,程序就会产生错误结果。还有这次教训惨痛的K题,忘记考虑了人数为0时的情况,又弄错了题意——选择恢复室首要看手术结束时刻,其次看手术室的编号,我把次要的理解为了花名册编号,好几次WA都出现在选择恢复室这个地方。
第三步,优化顶层设计,最初的顶层设计可能会很繁琐,需要想办法优化,使之变得更加简洁。比如这次训练的I题,要求模拟一个搜索引擎,给了四种搜索的方式,输出的时候根据情况要输出dash分隔符或equal分隔符。如果只是按照题意,先根据关键词建立词典库,再建立bucket存放每个关键词的位置,形成一个表,那么程序肯定会比较繁琐,繁琐的原因是你每次都要看是否有新的关键词需要搜索,最方便的办法应该是输入文章的同时就把文章中出现的单词的位置顺便更新了。虽然这样做占用的内存会比较多,但将来查询时候就会十分的方便,只需要O(N)的时间就能查询完所有出现的位置,最后再想办法单独处理分隔符的问题,而不是每种指令都处理一遍分隔符的问题。再比如这次的E题,模拟黑白棋的过程,寻找合法位置是一个棘手的地方,如果只是罗列出8个if语句进行处理,代码肯定会十分的啰嗦。正确而高效的做法是利用常量数组(本题中也可以成为是向量数组),事先存储好8个方向的方向向量,将来就能统一进行处理了:只需要沿着dx[d],dy[d]方向走就可以了。另外还想再说说这次的A题,模拟一个5*5棋盘中空格移动的游戏,题目中提到遇到非法指令时输出相应的错误提示。如果只是单纯地先存储了所有指令,然后再寻找,就比较浪费内存,因为可能某个位置的指令非法时,后面的一长串指令都是无效的,推荐的做法是一个一个指令地读,如果不合法,后面的直接continue即可。题目中输入的数据并不一定都非要事先存储下来才行。说了这么多,就想强调一点,要善于提取问题之间的共性和特性,学会集中处理问题。
第四步,查错并修改bug。应该尽可能多的测试各种可能的情况,思维一定要缜密,考虑周全,尽可能地多思考边界情况,比如这次K题我忘记考虑人数为0的情况。还有这次的A题,超时发生在判断结束符上,有时候你写的程序不能正确地读取到结束符的位置就会发生无限循环,最终导致TLE,这点也非常值得重视。
模拟专题暂时告一段落了,但今后还会不定时地训练这方面内容,毕竟是考验代码基本功的最佳题目,打牢基本功应该是每一位程序员必备的自我修养。