6 ]7 e8 i* |- L% r竞赛中至少命题6题,至多命题10题,比赛时间为5个小时,参赛队员可以携带诸如书、手册、程序清单等参考资料,试题的解答提交裁判称为运行,每一次运行会被判为正确或者错误,判决结果会及时通知参赛队伍,正确解答中等数量及中等数量以上试题的队伍会根据解题数目进行排名,解题数在中等数量以下的队伍会得到确认但不会进行排名,在决定获奖和参加世界决赛的队伍时,如果多支队伍解题数量相同,则根据总用时加上惩罚时间进行排名,总用时和惩罚时间由每道解答正确的试题的用时加上惩罚时间而成。每道试题用时将从竞赛开始到试题解答被判定为正确为止,期间每一次错误的运行将被加罚20分钟时间,未正确解答的试题不记时,地区预赛可以使用的语言包括C/C++和Java,每支队伍使用一台计算机,所有队伍使用计算机的规格配置完全相同。(竞赛具体的软件环境可能根据赞助商的变化而变)1 Y: p" [0 }6 j- [( ?: P+ f5 q+ f
. O! R: S& o) N6 w/ v9 V
二、关于竞赛的题型分析 / n+ x4 r4 C% n6 D9 s2 y! J3 W/ _Hal Burch通过在1999年春季的分析得出了这样的结论,竞赛的程序设计一般只有16种类型,它们分别是:! C' u( \5 X$ Q7 W/ p! c
o1 P/ n$ F: h2 j, A# \5 d% i) s0 N
Dynamic Programming (动态规划) 7 H" V8 f& d+ y$ u5 k! VGreedy (贪心算法) " b1 i* O2 u- I# a! {0 g
Complete Search (穷举搜索) - A' P% X- s" i( G5 t& B1 ]
Flood Fill (不知该如何翻译) + @' n- V$ \2 s) O$ s9 W9 y7 t2 ~Shortest Path (最短路径) + |" @6 p- ~2 `8 F& h! a5 G4 k
Recursive Search Techniques (回溯搜索技术) ( h4 D; Z2 W$ w% P6 U7 A
Minimum Spanning Tree (最小生成树) 8 a) z" H& Q4 [: x+ y+ v+ p: u3 T+ z- QKnapsack (背包问题) & D7 R2 ?/ f% w, k+ F- P9 F2 G
Computational Geometry (计算几何学) & A" h, _7 l7 f9 @6 b( n' K. HNetwork Flow (网络流) 3 @7 f$ q* d8 V. X" kEulerian Path (欧拉回路) 8 P9 I: f( L# k% R: wTwo-Dimensional Convex Hull (凸包问题) 4 J# n, c& y, S3 @
BigNums (大数问题) ' k: |& J# p# y' C1 h
Heuristic Search (启发式搜索) & U0 h2 x; k. f- O& }' TApproximate Search (近似搜索) 4 G" x. M, T* fAd Hoc Problems (杂题) ! b% U' L$ V# z! c0 r - `, C/ S+ j# v( F& H很少有人能真正掌握这其中绝大部分的方法,而对于一些包含了这些方法组合与循环的具有挑战性的综合问题,多数选手都无能为力,因为竞赛中的很多试题都需要选手当场作出分析,而不是套用固定的解题格式,这是竞赛的困难所在,也是它的魅力所在。 4 M/ [" \+ U q, E- Y b5 |) a* r3 L( D* i3 |
三、竞赛准备 " k9 z) B, P- c* W- [4 VACM竞赛不要求使用某一种特定的语言,所以各个队伍可以根据语言的特点和自己的特长选择,如果对语言的原理语法和特点均能做到成竹于胸、滥熟于心,在比赛的过程中就可以大大缩短调试的时间,从而获得优势。5 f& v1 P+ r& x( _; m' H
$ U' F. }, s2 B& h J8 {. X0 l# M
然而编程之道就如武学之道,语言只是各门各派的武功招式,算法和数据结构则好比内功心法和武学原理。内力深厚,任何招式到了手上都能够化腐朽为神奇;掌握了武学原理,更能做到无招胜有招。选手在竞赛中最重要的素质,正体现于对算法和数据结构的掌握和理解上,通过对经典问题的分析,掌握各种算法的应用范围和数据结构的作用与具体实现,是每个选手在平时学习中的重点所在。" [" w/ B1 c1 X& C, l% R
; [, m3 v! v0 ~; G# P
四、竞赛策略# w! N# r Z4 R5 i
临近比赛,在实力上已经难有质的提高,这时我们不妨将注意力转移到竞赛技巧方面,做不成武学道师也学个韦小宝。在ACM竞赛中,一般来说能成功解决半数或以上题目的队伍已经是相当优秀的,解决所有问题近乎天方夜潭,也就是说无论你的实力如何,都还有很大的改进余地,这其中比较重要的就是竞赛的策略。 # Y, C7 v) R+ A4 F- {3 ?0 M- v6 U' v1 v# R. u0 H3 x
(1)分工的问题: " q% d6 i! q0 Z2 |. o7 X团队的配合十分重要,三个队员之间的合理分工可以大大改进解题的效率,根据队员的不同特点,不同的队伍可以采用不同的分配方式,其间一些细节的处理需要三个人有很好的默契。 . i; b& O" ~2 v P( l7 k7 n/ h: G+ G4 E0 c2 ~+ B% W, P
(2)算法的选择: . t1 q( ]: D% a' E在所有可行的算法当中,我们选择的应该是最可行的方法,而不是最高明的方法,这是竞赛与解决问题的一个重要区别,按照熟悉的程度由高到低选择一个算法,通过计算算法的时间和空间复杂度(在必要的情况下)和特殊的测试数据找出一切使该算法不成立的理由,如果找不到就确定该算法并选用相应的数据结构。在确定思路的时候注意比较常见的思维方式分析,比如逆向的分析,对称的分析等等。" W$ z- L. N) _: a0 `4 j& p* j
+ Z) v( ^ _/ L1 s( v(3)程序的编写:0 u* r& b$ F0 n, }+ }+ D
最好首先编写输入和输出的部分,然后逐步细化,一个部分一个部分地填充调试,其间通过适量的注释来刻画程序的逻辑结构和特殊的技巧。在完成全部代码后用一般的测试数据验证代码的正确性,然后处理特殊的情况和边界问题,试图尽可能地找出错误的情况并加以改正。关于程序的优化主要考虑的是最坏情况下所用的时间是否满足要求,优化的程度以题目要求为准,足够即可,尽量避免使用指针和动态分配,在空间允许的情况下一律采用静态分配。9 n' [" E0 d; X: J- J' ~" ?
! k$ h t! Y G; l' P4 L! W2 d(4)调试中的问题:9 Z: _7 E/ n# g% _" o. k7 R* W
调试中会遇到的许多问题需要在事前有所准备并定出总体设计,当然具体的情况还要临场分析,考虑的方面包括程序中的BUG,算法的正确性和数据结构的合理性,什么时候该放弃这个问题,什么时候该返回到先前放弃的问题,是否需要做到或已经做到足够的优化等等。所有关于调试的输入输出都不要删除,将它们注释起来即可。/ i @ Z. j3 \8 R f
) C' \# C* G% V; \4 @4 w0 p(5)竞赛中的杂题处理- L' J5 g+ U; h/ E) d& J% X: u' W
在竞赛中有时会出现一些新颖的题型,解决它们的算法很难归到经典的算法中去,每个这类的题都有自己鲜明的特点,对于它们根本没有一般的解法。对于这样的挑战,一个新颖的数据结构或一套特殊的循环或判断常常是必须的。解决这种问题的关键在于仔细地阅读题目的叙述,灵感经常来自于将叙述的逻辑条理整理得十分清楚之后,同样,对这类题的优化也是需要的,至少需要避免过多的循环嵌套。2 m" }( E# g2 z1 z
B. w* f$ Y Y$ H$ v) G' N9 M
五、编程与竞赛6 F3 k; Y! t0 V8 C
学习编程并不是为了参加竞赛,竞赛对于多数选手的意义还是在于参与,以及在备战过程中对自己的锻炼和提高。在这一点上,ACM竞赛和其它一系列竞赛是一样的,只是它的影响力和规模大些罢了,所以笔者希望对编程有兴趣的同学都能够关注竞赛,即使不参加,通过了解竞赛中涉及的编程知识达到课内很难达到的高度,这对每个人都是有益无害的。/ ^! F$ ]# I ]! C" N
* w2 A7 D7 b6 V# m, r- V( `5 e
O0 S& C$ ~& s1 r1 C# }) ~6 f# f* U9 ^- t# J+ ]( |4 E
一、语言是最重要的基本功 D7 ^8 @ Z$ n& v- }; P9 n9 O8 w( Y7 I
无论侧重于什么方面,只要是通过计算机程序去最终实现的竞赛,语言都是大家要过的第一道关。亚洲赛区的比赛支持的语言包括C/C++与JAVA。笔者首先说说JAVA,众所周知,作为面向对象的王牌语言,JAVA在大型工程的组织与安全性方面有着自己独特的优势,但是对于信息学比赛的具体场合,JAVA则显得不那么合适,它对于输入输出流的操作相比于C++要繁杂很多,更为重要的是JAVA程序的运行速度要比C++慢10倍以上,而竞赛中对于JAVA程序的运行时限却往往得不到同等比例的放宽,这无疑对算法设计提出了更高的要求,是相当不利的。其实,笔者并不主张大家在这种场合过多地运用面向对象的程序设计思维,因为对于小程序来说这不旦需要花费更多的时间去编写代码,也会降低程序的执行效率。' {; V T1 ]# b$ X5 O, ]% R
, c% m- E1 o- G- \, @- G! a% u) k# Q
接着说C和C++。许多现在参加讲座的同学还在上大一,C的基础知识刚刚学完,还没有接触过C++,其实在赛场上使用纯C的选手还是大有人在的,它们主要是看重了纯C在效率上的优势,所以这部分同学如果时间有限,并不需要急着去学习新的语言,只要提高了自己在算法设计上的造诣,纯C一样能发挥巨大的威力。. ]' f' b, B! Z5 Y- E. p* b P3 y% q& q b- b
* R. f- c E# v. g1 w通过以上的分析,我们可以看出仅就信息学竞赛而言,对语言的掌握并不要求十分全面,但是对于经常用到的部分,必须十分熟练,不允许有半点不清楚的地方,下面我举个真实的例子来说明这个道理——即使是一点很细微的语言障碍,都有可能酿成错误:5 ^) u8 r- ~/ d9 S9 B1 K
% F8 T1 J( t4 h在去年清华的赛区上,有一个队在做F题的时候使用了cout和printf的混合输出,由于一个带缓冲一个不带,所以输出一长就混乱了。只是因为当时 judge team中负责F题的人眼睛尖,看出答案没错只是顺序不对(答案有一页多,是所有题目中最长的一个输出),又看了看程序发现只是输出问题就给了个 Presentation error(格式错)。如果审题的人不是这样而是直接给一个 Wrong Answer,相信这个队是很难查到自己错在什么地方的。; j5 n/ X( S" g# G1 c
* _$ B9 G$ c$ N
现在我们转入第二个方面的讨论,基础学科知识的积累。 : m5 z$ Q* h# [4 o* j/ a) g: T& ]+ s( w" M+ |! r; L
二、以数学为主的基础知识十分重要5 g4 \. y8 I8 z. T4 }5 I U" y
c7 z; n4 b! e/ E6 B8 e1 r
虽然被定性为程序设计竞赛,但是参赛选手所遇到的问题更多的是没有解决问题的思路,而不是有了思路却死活不能实现,这就是平时积累的基础知识不够。今年 World Final的总冠军是波兰华沙大学,其成员出自于数学系而非计算机系,这就是一个鲜活的例子。竞赛中对于基础学科的涉及主要集中于数学,此外对于物理、电路等等也可能有一定应用,但是不多。因此,大一的同学也不必为自己还没学数据结构而感到不知从何入手提高,把数学捡起来吧!下面我来谈谈在竞赛中应用的数学的主要分支。( L' k0 B0 y* m' S$ f# ^
0 v \" `$ [; M1、离散数学——作为计算机学科的基础,离散数学是竞赛中涉及最多的数学分支,其重中之重又在于图论和组合数学,尤其是图论。 ; ]4 b. K& A" H! Y+ l' S0 W4 z' K , Z7 w# d! i: H* v图论之所以运用最多是因为它的变化最多,而且可以轻易地结合基本数据结构和许多算法的基本思想,较多用到的知识包括连通性判断、DFS和BFS,关节点和关键路径、欧拉回路、最小生成树、最短路径、二部图匹配和网络流等等。虽然这部分的比重很大,但是往往也是竞赛中的难题所在,如果有初学者对于这部分的某些具体内容暂时感到力不从心,也不必着急,可以慢慢积累。 {# c# _2 ~" r) D1 Q
8 u3 `; O* x3 W1 |0 J x
竞赛中设计的组合计数问题大都需要用组合数学来解决,组合数学中的知识相比于图论要简单一些,很多知识对于小学上过奥校的同学来说已经十分熟悉,但是也有一些部分需要先对代数结构中的群论有初步了解才能进行学习。组合数学在竞赛中很少以难题的形式出现,但是如果积累不够,任何一道这方面的题目却都有可能成为难题。 K- i: g$ q, K; w, Q8 O! n+ n* s% u + Q7 [, A5 C o. l* w" h1 i2、数论——以素数判断和同余为模型构造出来的题目往往需要较多的数论知识来解决,这部分在竞赛中的比重并不大,但只要来上一道,也足以使知识不足的人冥思苦想上一阵时间。素数判断和同余最常见的是在以密码学为背景的题目中出现,在运用密码学常识确定大概的过程之后,核心算法往往要涉及数论的内容。 3 S) u- U0 ^& [8 ^/ |' C) { B9 i5 s3 J1 `, B- f( F
3、计算几何——计算几何相比于其它部分来说是比较独立的,就是说它和其它的知识点很少有过多的结合,较常用到的部分包括——线段相交的判断、多边形面积的计算、内点外点的判断、凸包等等。计算几何的题目难度不会很大,但也永远不会成为最弱的题。 8 i' J P- ~& g1 \' D H4 F1 T& Z/ _* q. z
4、线性代数——对线性代数的应用都是围绕矩阵展开的,一些表面上是模拟的题目往往可以借助于矩阵来找到更好的算法。 1 o; W. i$ ^ t e3 S6 U1 F9 x ?5 t# u) Q% e' W! L
5、概率论——竞赛是以黑箱来判卷的,这就是说你几乎不能动使用概率算法的念头,但这也并不是说概率就没有用。关于这一点,只有通过一定的练习才能体会。 - I4 f2 K+ n* G: v* U ) _: o: t' Q _' }+ l- ?: V/ M" P6、初等数学与解析几何——这主要就是中学的知识了,用的不多,但是至少比高等数学多,我觉得熟悉一下数学手册上的相关内容,至少要知道在哪儿能查到,还是必要的。 4 \* l- ^* s& b0 k4 C8 ~ b1 ~2 F( x" o
7、高等数学——纯粹运用高等数学来解决的题目我接触的只有一道,但是一些题目的叙述背景往往需要和这部分有一定联系,掌握得牢固一些总归没有坏处。- z( T0 ~7 d& A, ?: A
' \) V$ @2 c, ~( @$ x6 Y6 k1 l
以上就是竞赛所涉及的数学领域,可以说范围是相当广的。我认识的许多人去搞信息学的竞赛就是为了逼着自己多学一点数学,因为数学是一切一切的基础。2 \4 ^$ `& w* E% H0 M2 [
; O6 a& ^' u( r6 u9 G$ s# E0 N
三、数据结构与算法是真正的核心 * q8 T" S0 A6 {! t0 h& ]4 t! T4 t. [3 m: V! j; _' t$ j
虽然数学十分十分重要,但是如果让三个只会数学的人参加比赛,我相信多数情况下会比三个只会数据结构与算法的人得到更为悲惨的结局。 ) v5 n y/ r- k4 n5 K 4 Z2 U2 h" }( a1 H& i先说说数据结构。掌握队列、堆栈和图的基本表达与操作是必需的,至于树,我个人觉得需要建树的问题有但是并不多。(但是树往往是很重要的分析工具)除此之外,排序和查找并不需要对所有方式都能很熟练的掌握,但你必须保证自己对于各种情况都有一个在时间复杂度上满足最低要求的解决方案。说到时间复杂度,就又该说说哈希表了,竞赛时对时间的限制远远多于对空间的限制,这要求大家尽快掌握“以空间换时间”的原则策略,能用哈希表来存储的数据一定不要到时候再去查找,如果实在不能建哈希表,再看看能否建二叉查找树等等——这都是争取时间的策略,掌握这些技巧需要大家对数据结构尤其是算法复杂度有比较全面的理性和感性认识。: C: D4 O1 f$ C% d
; s0 g, S) M( ~$ T0 m0 v接着说说算法。算法中最基本和常用的是搜索,主要是回溯和分支限界法的使用。这里要说的是,有些初学者在学习这些搜索基本算法是不太注意剪枝,这是十分不可取的,因为所有搜索的题目给你的测试用例都不会有很大的规模,你往往察觉不出程序运行的时间问题,但是真正的测试数据一定能过滤出那些没有剪枝的算法。实际上参赛选手基本上都会使用常用的搜索算法,题目的区分度往往就是建立在诸如剪枝之类的优化上了。. k. y$ b; d4 }7 f) V! Z' W
& b; m2 O" b9 D$ Y; e3 C& ~
常用算法中的另一类是以“相似或相同子问题”为核心的,包括递推、递归、贪心法和动态规划。这其中比较难于掌握的就是动态规划,如何抽象出重复的子问题是很多题目的难点所在,笔者建议初学者仔细理解图论中一些以动态规划为基本思想所建立起来的基本算法(比如Floyd-Warshall算法),并且多阅读一些定理的证明,这虽然不能有什么直接的帮助,但是长期坚持就会对思维很有帮助。" d7 n. L3 Y% i- V0 H1 C4 V
$ O. f+ z. U. [3 U' J, W四、团队配合 # y' {. y$ w7 A: @ * E, S$ I, x$ m( z' a# d1 r6 T w通过以上的介绍大家也可以看出,信息学竞赛对于知识面覆盖的非常广,想凭一己之力全部消化这些东西实在是相当困难的,这就要求我们尽可能地发挥团队协作的精神。同组成员之间的熟练配合和默契的形成需要时间,具体的情况因成员的组成不同而不同,这里我就不再多说了。7 U5 _7 `. X' F' ^