2006年,一个计算机系本科生,人生第一次也是唯一一次 ICPC。四个半小时,十道题,零。
不是"发挥不好"。记分榜从下往上数,第一行就是他们三个。
连他读完题心想"这不送分吗"的那道基础数学题,也没过。
根因他直到比赛结束前几分钟才想明白。跟题目没关系。
【灯光渐暗,三个人围着一台机器,屏幕上第十一次 Memory Limit Exceeded】
这个事故干净得值得单独讲:他们的卡死方式高度统一——读完题,觉得能做,敲进去,提交,MLE。改算法,再来,还是 MLE。换一道题,一样。那四个半小时就是一场标准渡劫,只不过从头到尾没出过一次"答案错误",也没出过一次"超时"。就是内存,反复地内存。
先插一句题外话。Fayaz 自己在文章里交代过,他是慢热型选手,必须先把概念彻底嚼碎才敢动手,天生不适合竞技编程。这话我信。所以那场比赛他唯一做对的决定,是两个朋友拉他组队、请他当队长的时候,他婉拒了。
剩下全是错的。
他们赛前的评估做得其实挺认真:刷往届题,算法大多熟,主要练手速;还一起打了一场 Topcoder 线上单人赛,三个人都做出三道里的两道。凭这个,他们给自己定的目标是解出五到六成——不冲名次,只求别太难看。
这个预测模型回头看,唯一的偏差是实际值:0。
凶手呢,前面说了一半——那段输入输出代码。赛前用 C 写的,功能朴素得不行:把所有输入一次性读进一个大数组,算完拼成一个巨型字符串,一把吐出去。练习用它,之前两次比赛也用它,从来没炸过。
所以那四个小时里,他们的怀疑名单上有算法、有思路、有队友的临场判断、有自己的智商——
唯独没有那段代码。因为那是自己写的。
"自己写的"这四个字,是全宇宙最便宜的一张免检通行证。付一次,管很久,久到你都忘了它还在免检期里。
这个盲区我太熟了。真不是"不够仔细",恰恰相反,是太仔细了,仔细到把全部注意力都押在"这题解法对不对"上。那段模板早就不在视野里了,它跟键盘、跟电源线、跟机房的椅子是同一类东西:背景音。
背景音不出事的时候,没人会想起来检查。
作者在文里补了一句,说如果当年有 Git 之类能存不同版本的解法,他们也许能回退到更简单的版本、把 I/O 那块修掉,最后几分钟至少能抠出一题。
这个我不太买账。
Git 能救的是"改坏了",救不了"从来就没对过"。你要回退,前提是你知道该退到哪一层——而他们当时连错误出在算法的哪一层都不知道,退回上一版,那段 I/O 还是原封不动跟着你,一样炸。工具解决的是版本问题,不解决视野问题。
("要是有 XX 就好了",是事后复盘里最舒服、也最没用的一句话。这句我先存着,下次自己复盘的时候拿出来打自己的脸。)
然后是我看这篇文章最想笑的地方。
文里用来演示那次内存超限的示例代码,不是当年的真代码——作者在文末写明了,是 Gemini 生成的近似示例。合理,二十年前的代码谁还留着。
结果评论区有人动手算了一遍账:那两个全局缓冲区加起来大约 7.15 MiB,而且每个测试用例复用同一块,110 个用例根本不会把这块内存累加。也就是说,这段用来演示内存超限的代码,自己的内存账算错了。
用来演示内存超限的示意代码,自己超不了限。
AI 蒙对的时候我们管那叫显灵,蒙错的时候我们管那叫正常发挥。
第一次看完我笑了。第二次再看,觉得它错得太真实了——它和二十年前那段模板犯的是同一个错:都长得很工整,都很有自信,都被扔进了"这个不用再看"的格子里。
区别只有一个:2006 年那张免检通行证是你手写的,现在是它替你写的。
【关于本次"AI 示例代码不算数"的裁定】作者本人回复得也坦荡——AI 代码只用于演示,别当真实证据。他顺手把真账补上了:当年实验室机器可能就 64MB 内存,竞赛限制大概十来二十兆,大输入光读进去就快 20MB。所以那次是真炸了,只是示例没演示对。
我喜欢这个补充。真实的事故从来不配合你的示例代码,它的数字永远比你的假设脏。
最后那个细节才是整件事里最扎人的。他们试了将近三个小时,然后放弃,剩下的时间在聊天。就在结束前几分钟,他眼睛一挪,突然想通了。
不是灵光一闪。是终于不再盯着算法看,余光扫到了旁边那段从来没正眼看过的代码。
那个时刻本该是全场最爽的一秒。可惜裁判不给加时。
后来他又和这两个人组队打了一场非 ICPC 的校际赛,拿了第二名。那是他们最后一次以队伍形式参赛。
这个结局挺好。不是因为它励志,是因为它说明那四个半小时不是终点,只是一笔贵得离谱的学费——贵到二十年后还值得写一篇文章。
(郑重声明:写作期间我没参加任何比赛,纯属围观别人的事故并获取快乐。你手边大概也有一段"从来没出过错所以从来不用看"的代码,别找了,你找不到的——它只会在你哪天眼睛挪开的那一秒,自己跳出来。评论区扣个"同款",下条再见 🫠)
