信息学奥赛一本通1062
《信息学奥赛一本通1062》这道题,信息学奥本质上是赛本在问一个朴素的问题:给你一串数字,怎么找出里面重复出现的信息学奥那一个。听起来简单,赛本但放在竞赛里,信息学奥它考的赛本不是“能不能找到”,而是信息学奥“你打算用多少时间找到”。
题目给的赛本是n+1个数,范围从1到n,信息学奥恰好有一个数出现了两次。赛本最笨的信息学奥办法是双重循环,一个一个比过去——n最大能到10万,赛本n²的信息学奥复杂度直接让程序跑到天荒地老。我见过不少新手卡在这里,赛本盯着屏幕发呆,信息学奥以为是自己代码写错了,其实只是算法选错了路。
换个思路就豁然开朗。既然数字范围是1到n,总共n+1个数,那不如开一个布尔数组,挨个标记。每读到一个数,先查它是不是已经被标记过——如果标记过了,那就是那个重复的倒霉蛋。整个过程只需要扫一遍,时间复杂度O(n),空间也就多出一个n大小的数组。代码短得惊人,核心就十几行。
但你要是以为这题就这么点东西,那就太小看它了。真正有意思的地方在于,有些解法连额外数组都不用。比如用数学办法:把1到n加起来,得到一个理论总和;再把实际输入的数全部加起来,两者之差就是重复的那个数。这法子巧妙归巧妙,但有个隐患——如果n特别大,累加可能溢出。竞赛里没人提醒你这些,得自己掂量。
我当年做这题的时候,第一次提交用的是排序法。先把所有数排个序,然后相邻两个相等就找到了。逻辑没错,可排序本身就要O(n log n),虽然能过,但总觉得不够痛快。后来看了别人的题解,才发现布尔数组那招,简单得让人想拍大腿。
这题最磨人的地方不在算法本身,而在读题。很多人没注意到“恰好有一个数重复”这个条件,以为可能有好几个重复,于是写出一堆防御性代码,把自己绕晕了。竞赛题就是这样,信息都给你了,但藏在字缝里,漏看一个词,思路就跑偏。
代码写出来容易,难的是想清楚为什么这么写。布尔数组法之所以高效,是因为它利用了“数字范围有限且连续”这个特性——这恰恰是题目给的隐含线索。你要是把n换成任意大整数,这法子立刻失效。所以啊,算法不是背模板,是看清题目到底给了你什么牌。
最后说句实在话,1062这题在整本《信息学奥赛一本通》里连中等难度都算不上,但它是个很好的分水岭。能一次想明白的人,多半已经理解了“用空间换时间”的代价与收益;还在纠结的人,不妨停下来,把数组下标和值的关系再嚼一嚼。这题过了,后面很多题的路就顺了。