编程是开发和实现一系列指令以使计算机执行特定任务的过程。像 ACM ICPC、Google CodeJam 和 IEEE Extreme 等编程竞赛,都是程序员探索智力智慧的绝佳场所。从最初了解比赛到站在 ACM ICPC Amritapuri 区域赛的赛场上,我学到了很多,并希望与大家分享一些应对竞赛题目的技巧。
在实时的竞赛中,由三名学生和一台电脑组成的队伍需要在 5 小时内解决尽可能多的题目。解题数量最多的队伍获胜,这里的“解决”意味着能为一组测试输入(包括常规测试和隐藏数据)生成正确的输出。虽然队员的个人能力很重要,但要想成为顶尖队伍,必须充分利用团队内的协同效应。
然而,为了充分利用策略,至关重要的是将个人技能磨练到最佳状态。你不必非得是天才,因为练习就能让你走得很远。就我的感觉而言,成为一个优秀的编程团队有三个关键因素:
- 掌握标准算法,并能为题目集中的每个问题找到合适的算法
- 将算法编码为可运行程序的能力
- 与队友制定合作策略
什么是算法?
算法 是计算机要遵循的一步一步的指令序列。
要想在编程竞赛中有所作为,你需要了解很多众所周知的算法,并且能够识别出哪个算法适合特定的问题(如果问题比较直接),或者哪些算法的组合或变体适合(如果问题稍微复杂一些)。
快速识别问题类型的能力
在所有编程竞赛中,只有三种类型的问题:
- 我以前没见过这个问题。
- 我以前见过这种类型,但没解出来,或者解不出来。
- 我以前解决过这种类型。
在编程竞赛中,你将面对一系列问题,而不是单个问题。能够快速将问题归类到上述背景分类(没见过、见过、解决过)中,将是在编程竞赛中取得好成绩的关键因素之一。就我的知识和储备而言,一个问题通常在给定的类别中进行分类:
- 数学 : 质数、大整数、排列、数论、阶乘、斐波那契、序列、模运算
- 动态规划 : 最长公共子序列、最长递增子序列、编辑距离、0/1 背包、硬币找零、矩阵链乘法、最大区间和
- 图遍历 : 泛洪填充、Floyd Warshall、最小生成树 (MST)、最大二分匹配、网络流、关节点
- 排序: 冒泡排序、快速排序、归并排序、选择排序、基数排序、桶排序
- 搜索 : 完全搜索、暴力破解、二分查找
- 字符串处理 : 字符串匹配、模式匹配
- AdHoc 问题: 简单问题
分析算法的能力
你已经识别了你的问题。你认为自己知道如何解决它。现在你必须问的问题很简单:给定最大输入边界(通常在问题描述中给出),我的算法在我能计算出的时间复杂度下,能否通过竞赛中给定的时间限制。
通常,解决一个问题不止一种方法。然而,其中一些可能是不正确的,还有一些可能不够快。然而,经验法则是:头脑风暴出尽可能多的算法——然后选择其中最“笨”(最简单/最稳妥)但能行的那个!
记住以下顺序对于快速确定哪种算法在渐近性能上更好非常有用:常数 < log n < n < n log n < n^2 < n^3 < 2^n < n!
编写问题代码
遵循 KISS 原则:保持简单和明智,保持所有版本可用,并逐步完善代码。
在你用最佳算法编写代码,匹配时间/空间复杂度之后…