ACM竞赛策略
(一)竞赛策略
1.先选择算法
~~~~~~
一道题一定要想清楚思路和算法细节后再写,想一想有没有更优的实现方式,特别是边界条件处理的这种。写题目前最好先估算下时间复杂度(会不会超时)
2.看题顺序
~~~~~~ 如果队伍中存在 2 位可以独立读题的同学,建议一人正序从第一题开始看,一人逆序从最后一题开始看,看不懂英文的同学可以帮助团队先打开编译器、敲好头文件、做好准备工作。前期可以同时开两题,水题和签到题的 AC 速度就是优势,需要尽快建立
3.心态要稳
~~~~~~ 前期水题和签到题心态要稳,一定要理解题目意思,注意极限数据,避免不必要的罚时。宁可晚 1分钟交,也不要白白挨上 20分钟罚时
~~~~~~ 一道题过的人数就说明了一道题的难易程度,如果一道你认为思维复杂度不高的题过的人数很少那一定是你想简单了
~~~~~~ 某题出现很多人提交错误,但是看起来又比较简单的题,一定要再仔细想一想此题是否有坑。当自己也提交错误时,在没有发现明显的坑之前,不要再盲目提交。这样容易造成自己的心态崩溃,也会对自己最后的成绩造成很大的影响
4.看榜单策略
~~~~~~ 由于本科组的同学过题速度比较快,可以选择跟本科组同学的榜单(优先选择本科组同学过题较多的题目)。切忌不要跟中学生的 榜单,例如学军中学、杭州二中、镇海中学等,他们的学习方法与大学生不一样,他们会的我们不一定会
5.数学,思维
~~~~~~ 如果其他专科组队伍短时间内过的题,而自己队伍想出来的解法实现较困难或者写代码的时间较长,则一定是没有考虑到更加方便的正解(往往这类题目涉及到数学、思维),请再仔细想一想更加简单的写法
6.构造数据
~~~~~~ 比赛中期时,专科组普遍开始出现题数差距,自己的队伍也很有可能开始出现 WA 等错误,这时候心态要稳住,专科组往年的差距都不会太大,要坚信人家能出的自己也一定能出,这时候可以有一位专门负责构造数据(极限数据、边界数据)的同学来构造数据(找反例),然后进行测试找出 bug
7.团队力量
~~~~~~ 前中期主代码手写代码时,另外两人需要快速定位到下一个可以打的题,并得出初步结论。理想状态是主代码手 AC 一题后立刻可以与队友沟通,短时间内突破下一题。切忌独自一人死扣一道无人AC或者 AC人数较少的题目
8.封榜后
~~~~~~ 比赛封榜后,如果全场过题数 >= 200 的题目,一定是简单题, >= 100 的题目,一定是可以做的题目(例如思维、数学、简单算法)
封榜这一个小时,就是逆风翻盘的最好时机,这最后一个小时内罚时不重要了,只要能过题,就是题数碾压 ,尝试你能写出的所有代码和猜想(甚至可以把cin,cout 改成 scanf,printf 直接交一发)
(二)代码注意事项:
1.数据范围
注意中途溢出
// b * b 就是在过程中溢出了
long long a;
int b = 100005;
int c = 2;
a = b * b /c
2.TLE
~~~~~~ 如果有题目你认为时间复杂度是合适的,但是提交结果是 TLE 的话,考虑是否使用了容器操作。例如 vector,map 等,如果使用map 且没有排序需求的情况下,可以考虑替换为unordered_map,如果是 vector,可以考虑替换为数组
前提是你的程序时间复杂度一定是符合题目要求的。
3.流加速
~~~~~~ 使用 cin,cout 必须使用流加速,避免不必要的超时。在主函数开头加上如下三行即可
ios::sync_with_stdio(false);
cin.tie(0); //输入加速
cout.tie(0); //输出加速
注意! cin,cout和scanf,printf绝对不可以混合使用
4.memset超时
~~~~~~ 如果一道题的极限数据是 1000000,你需要开一个相同空间的数组的话,在初始化数组的时候禁止使用 memset。而是应该使用手动初始化。因为假设后台数据共 100 组,只有 5组极限样例,如果你使用 memset,那么将跑 100 次1000000 的初始化,极有可能造成TLE
自己搞一个变量记录此次循环到哪里就好了,不需要每次都全部清空
各种数据无限对,但是一直 WA,检查一遍初始化
5.线段树
~~~~~~ 如果有线段树题目,记得建树时数组空间要开到 4倍的空间,比如总共 250000 个元素,那么数组就开到 1000000
6.图论
~~~~~~
例如最短路径,注意考虑负权边,并且一定
要读清题意,因为很有可能会出现起点就是终点这一类的极限数据
~~~~~~ 带有“路径”(path)字眼时,注意考虑path 为空、path 为单点的可能性
~~~~~~ 注意边的有向性,即“directed edge”,“undirectededge”
7.DP用滚动数组
~~~~~~ 使用滚动数组压缩空间复杂度
8.TLE可能时数组越界
~~~~~~
代码提交后返回 TLE,有的时候不是因为算法超时,而是可 能是越界,如果时间复杂度认为符合题目要求,可以仔细检查。
例如 HDU-1166,线段树题目因为建树时没有开辟 4 倍空 间,
返回的不是 RE而是TLE,所以一定要注意
9.段错误
~~~~~~ Segmentation Fault 则代表越界,请检查是否存在数组、容器越界或者堆栈溢出
10.vector记得清空
~~~~~~
如果用 vector 存答案的话,用之前 clear 一下,即使是声明
在函数栈里的也会有奇妙的问题
~~~~~~ 一般的STL容器都可以用clear清空,如果容器不能,那就一个个弹出来清空
11.打表
~~~~~~ 数据少,但是结果以及运算量特别大的题目可以考虑打表以及特判,就是在循环输入的外面先把数据都存到一个数组当中
~~~~~~ 禁止在循环内使用计算长度的函数或方法,需要在循环开始前提前计算好并且缓存,否则可能导致超时
//错误实例:
vector<int> s1;
char s2[10005];
for(int i = 0 ; i < s1.size() ; i ++)
//正确实例:
vector<int> s1;
char s2[10005];
int len1 = s1.size();
for(int i = 0 ; i < len1 ; i ++)
只是一个小问题,但追求更优美的代码
12.数组开外面
~~~~~~ 开较大的数组必须要开在全局上,不得开在函数内部,否则可能会导致栈溢出等问题
13.复制样例
~~~~~~ 输出的答案中包括类似 Case,Yes,No 等英文单词以及句子的时候,一定要复制粘贴,自己手打容易出错产生罚时。还要注意看清楚标点符号
14.判断奇数
if(n % 2 == 1)
no no no,上面的做法在n为负数时会得出错误的答案
以下为靠谱的做法
if(n % 2 != 0)
以下为更优美的方法
if(n & 1 == 1)
keep going, don’t settle.