开始你的ACM-ICPC之旅(转)

 

我觉得这样的文章应该有人写过的,但是Google里面貌似没有(或许有英文版)
Baidu给了一个,不过不是很像样
http://baike.baidu.com/view/94274.htm
那我就写一个吧,这也是momodi大牛在上个学期初委托给我的一件事情。

这篇文章面向的对象是没有多少基础,或者是才学C语言或数据结构的同鞋们。

--

首先,什么是acm/icpc?ACM/ICPC(ACM International Collegiate Programming Contest, 国际大学生程序设计竞赛)是由国际计算机界历史悠久、颇具权威性的组织ACM(Association for Computing Machinery,国际计算机协会)主办的,世界上公认的规模最大、水平最高的国际大学生程序设计竞赛。
——这段定义来自 百度百科 -> ACM/ICPC,其实说简单了就几个字:想算法,写程序,解题目

虽然要先想算法(解决问题的有效办法),但是如果不会写程序,想出来的算法通常会马头不对牛嘴:因为你的脑子不是电动的,不是用电脑的方式去思考怎样解决问题,通常这并不能有效地转化为计算机语言源程序(当然,不是说学会了写程序你的脑子就是电动的了)。写程序的过程,其实就是把人脑当电脑去和电脑说话,让它能够明白它要作的事情。所以,废话总结了就是,要先学计算机语言,这样你才可以和计算机打交道,让它去解决你想解决的问题。

acm/icpc正式比赛支持3种计算机语言,它们分别是c, c++, java。对pascal的支持已经被取消,但是大多数在线评测系统仍然支持pascal。如果没有学过计算机语言,那么你需要挑其中一种开始;所以如果你学过其中某一种语言,马上就可以开始了。不过,无论会或不会,这里仍然建议学习C++。C++是C语言的徒弟,不但学会了C语言的灵活性,还有自创了很多新功夫以方便程序员,比如面向对象机制、STL(标准模板库)等。另外,java也是值得学习的,因为它C++的徒弟,虽然笨了一点,但是它是完全的面向对象语言,还有一些C++没有自创的功夫,比如说BigInt(高精度整数类),如果用C++来实现,需要多写数百行代码(如果题目要开方,你就哭去吧),但是在java里面,只需要简单的一个import以后就可以使用了。

学习计算机语言,其实很简单——写来写去就那几个单词阿,泡MM还不知道得学多少花言巧语呢,所以,把电脑当MM来泡,嗯。这是一个转变你思考问题方式的过程,甚至对你处事方式都会有很大的影响。下面以c/c++为例展开。

找一本好书开始,是非常有必要的(因为上补习班很贵,嘿嘿)。虽然上面推荐学习C++,但是不妨从C语言入手,因为C会的功夫最少,入门最快(C++的秘籍里面总有很多让新手眼花缭乱、匪夷所思的词语),但是浓缩就是精华,想学[精]C的功夫,也不容易。这里推荐的是清华大学出版社 谭浩强老师的《C程序设计》(已经有第三版了)。有了一本好书,就可以开始了。

然后给自己找一个舒服的写代码的软件,通常我们叫它 IDE, Integrated Development Environment,集成开发环境。初学者当然会想找一个容易快速上手的IDE,那么这里推荐Devcpp,轻巧免费(免费!)易用,当然,微软的Visual Studio也是很不错的,它的调试功能很强大,设计也比较人性化,不过就是老掉牙的VC6有点不够符合C/C++的标准(VC2005以上OK了)——而且也忒贵了一点(顺便说一下M$的DreamSpark计划,可以让学生免费使用正版Visual Studio 2008,详情百度一下)。如果你有挑战自己的信心,那么最好的解决方案无疑是用最NB的[Vim/Emacs] + [gcc/g++]。vim和emacs都是强大的编辑器,搭配上同样开源免费的gcc/g++编译器,在熟练使用以后,能够明显提高你编码的速度(有一种说法是,世界上的程序员分三种:一种使用Emacs,一种使用Vim,第三种使用其它编辑器)。然后你可以打开教材,翻翻看C语言的历史,它的优点,并在第一章里面找到最经典的Hello World的源代码,在你的IDE里敲出并运行你写的第一个程序。

接着在开始正式地学习语言,但请先牢记所有人的切身体会:学语言,一定要动手写。学过英语的人都知道,一个单词能念出来,不一定能写出来——相信是beleive还是believe,收到是receive还recieve?如果没有多写,你可能永远分不清。学语言也是一样:定义一个结构体(struct)的时候,最后一行是否要加上分号?在struct的大括号外面是否要加括号?typedef定义是怎么搞定的?for语句里面i要从0开始还是从1开始,中间的哪个引号是可以省略的?——这些都是初学者容易搞混掉的东西,只有实际动手去泡电脑,发现了自己的错误,才能够真正记住。在这里没有列出答案,是因为这些问题的答案需要你自己去发掘。——只把书翻烂的是傻子,能把键盘磨烂的才是大牛

实际动手写,OK,写什么?有很多可以写的。最直接的,教材上的每一个例题:当你看懂了以后,脱离教材,自己把它写一遍。这样的练习是非常必要的,它能够为你打下良好的语言基础——你一定不想在检查完几百行代码以后发现错误只是因为数组的边界问题没搞清楚,甚至你自己根本无法发现你的错误。所以不要吝惜你的键盘(很便宜的,俺们都用Dell8115)。在学习C语言的过程中,最难搞定的应该就是要打通任督二脉:数组和指针。这也是C/C++功夫的精髓所在。C/C++之所以NB,是因为指针,灵活、高效;之所以被诟病,也是因为指针(程序员很容易滥用指针导致内存泄露、访问错误等,电脑MM也有隐私的,别乱看)。所以在这里不妨慢慢地学,多想,多写,心急吃不成热豆腐,生米煮成熟饭也是需要时间的嘛。链表多写几次;写一组用来处理矩阵的函数(或封装为一个对象)。如果时间宽裕,最好还能做些课后的习题。此外,去泡各大OJ(Online Judge,在线评测系统,比如
http://acm.whu.edu.cn/woj武汉大学在线评测系统)的服务器也是很赞的,因为OJ上的简单题目也是非常好的练手方案,这里有WOJ的大部分简单题列表http://acm.whu.edu.cn/blog/tag.php?tag=%25E5%2585%25A5%25E9%2597%25A8。做OJ的好处是,如果你的代码中有不严谨的地方,很容易被OJ上高强度的测试数据检查出来。

初学程序时,书上的例子很容易会有地方看不懂,是很正常的,这也正是转变你思维方式的开始:试着用电脑的方式来"读"程序。就是把你脑子训练成CPU,一步一步地"执行"程序,有需要的话可以用纸张当作内存,"存储"程序运行中的变量的值。这样你就会很快地搞清楚电脑MM心里是咋想的,泡起来也就容易多了。这对于训练自己的思维方式是非常重要的。有许多人在学习C语言一两年以后仍然觉得把自己的想法转化为程序代码非常困难,即使是对着清晰地描述出来的具体实现步骤——不是因为他们不懂语法(他们可能有接近满分的C语言考试成绩),而是他们根本没学会电脑MM的"思维"方式。当你学会了这种思维方式以后,你就能很快地把你的想法转化为程序代码了。

在写代码的过程中需要注意一个问题:代码风格。首先对比一下下面两段代码:
int main(){char a[100];
for(int i=0;i<10;++i)
{
scanf("%s",a);
       printf("Hello %s!\n",a);
}
return 0; }


int main(){
char name[100];
for(int i = 0; i < 10; ++i){
       scanf("%s", name);
       printf("Hello %s!\n", name);
}
return 0;
}

喜欢哪一段代码?你一定不会昧着良心说喜欢第一段吧:话都说不清楚还好意思去泡MM?虽然第一段代码是故意那样写的,但是并没有丝毫夸张。因为在初学者的代码里经常会见到这样的"风格"。初学者往往对此不以为然,他们的理由通常是:这段代码很短,没什么关系;以后再改,没关系;看起来没啥区别阿。上面那段代码确实很短,即使像第一段那样也容易读懂,但是你不可能永远只写四五行的程序的。当程序变长以后,问题就来了:哎呀,编译错误了!咋回事捏?噢,这里漏掉一个小括号,那里漏掉一个大括号。哎呀,运行结果不对了!咋回事捏?找了半天没找到,发给大牛,被BS了:就这代码,好意思让大牛帮忙看阿,想泡MM也要有点自知之明阿!(过了些日子回头无意中看到自己的代码)这代码TM谁写的阿,这么恶心咋看阿?——所以,从敲出的第一行代码开始培养自己的代码风格,利己,利人。代码风格里面主要需要注意的几点是:1. 缩进,所有同一层次的语句缩进相同;2. 空格和空行的合理使用,使得代码清晰易读;3. 适当的注释,让代码的阅读更加简单;4. 括号的使用;5. 变量命名应该规范,含义清晰。具体的内容,baidu和google会告诉你。请一定记得这句老话:磨刀不误砍柴工

终于,代码写好了,编译也OK了,双击它,运行了!崩!地址0x00000000访问错误,该内存不能为read(这位小哥注意点,MM在里面换衣服呢)。当然,错误可能还有很多种,最常见的是输入对的,输出不对(又是废话)。举例来说,目的是求一个一元二次方程的两个实根,思路是清晰di,算法是明确di,程序是整洁di,就TM答案是错误di。咋回事?调阿!——不是程序调戏你,是你要去调戏程序,哦不,是调试程序。泡电脑MM的大牛们说,不会调试的程序员永远写不出好程序,真正的好程序是调出来。调试,是调试,嗯。是个人都知道程序有问题了,那问题到底再哪里呢?这就是调试的工作了。说白了就是找茬嘛。可以在程序中手工插入一些语句来实现暂停以及输出中间变量的值来判断程序在某一步是否出现了问题,以便定位错误;也可以依靠IDE或者其他调试工具(如gdb)提供的调试功能,在不修改源代码的前提下按你的意思在一个可控制的环境中运行一个程序(例如,你可以一次运行程序的一行代码,检查变量的值,改变这些值,或者让程序运行到某个定点然后停止等)。总而言之就是定位错误,为解决问题提供分析所需的重要线索。至于定位了问题以后,就不能再把人脑当电脑了,要好好想想为什么会出现这样的问题。实在想不出来,就去请教能把电脑泡得服服贴贴的大牛吧。慢慢地积攒经验,你也可以搞定她。

此外:虽然建议从C语言开始,但是最好能在开始写代码以后能尽早接触面向对象的相关知识,这对之后的编码是很有帮助的。

有人说,连C++的语言之父都要别人告诉他C++的用法,所以语言的学习是不会有止境的。在打好的坚实的语言基础之后,可以看一些更深的书籍,推荐几本好书,可以挑着看:《The C Programming Language》《The C++ Programming Language》《C++ Primer》《The C++ Standard Library》 《C++入门经典》。其中The C++ Standard Library主要讲到了STL,很值得一看(STL这么有用的东西不学,会遭天遣的...)。

--

有很多人有这样的疑问:现在我已经可以把电脑MM泡到脚软,让她帮我,额,解一元二次方程组或者统计一个文件里面单词的数量,但是如果遇到更复杂的,比如说走迷宫,找出两个字符串最长的公共子串这样的问题——完蛋,我MM在迷宫里面迷路了,又没有食物,咋办?啥,烤字符串?毛,你个棒槌,人家mm要电动的。所以还是学习算法,把MM救出来现实一点。在学习了语言的基础上,这里可以狭隘一点地看待算法这个概念:一种有效的组织计算机程序和数据并对数据处理以求解决遇到问题的办法。组织计算机语言已经会了,但是还得把数据组织好阿。给一个简单的问题,对100个数据排序,要怎么组织?定义100个变量? int a001, a002, ......, a100?你当然不会这么笨:学过数组的嘛!对,但是就像上面提到的走迷宫,这算法需要怎么组织其中用到的数据?迷宫本身可以用二维数组来保存,但是走迷宫时走过的步子以及遇到的岔道这些,要怎么组织起来?这里需要用到栈或者队列这些更高级的道具——这就属于 数据结构,Data Structure 的范畴了。

百度百科 -> 数据结构 说:俺是计算机存储、组织数据的方式。俺是指相互之间存在一种或多种特定关系的数据元素的集合。通常情况下,精心选择的俺可以带来更高的运行或者存储效率的算法。俺往往同高效的检索算法和索引技术有关。俺在计算机科学界至今没有标准的定义。

Orz...看起来真累吧,咱是要来泡MM的,不是来听天书的。简单点说,那就是要搞清楚怎么样在计算机中组织数据(存储,读取,处理……)的一门学问。这和算法是相辅相成,密不可分的。某种数据结构的构建本身就需要算法的支持;一个好的算法如果没有合适的数据结构支撑,就像大力水手吃白菜,也就能顶个P用。

学习数据结构,当然也需要一本好书(原因就不说了吧,省得有人说俺财迷),这里推荐清华大学严蔚敏老师的《数据结构》,或者《数据结构 C++ 语言描述》。不论哪本,你都会学到最大众化的数据结构,诸如顺序表、栈、队列、串、树、图等内容。至于具体怎么学——看书去(p.s.建议学习图的时候跟进学习离散数学里面的图论)。想要学好算法,这些数据结构必然是要熟练掌握的,而想要熟练掌握这些数据结构——自古华山一条路,写。在理解了某个数据结构的实现方法以后,自己写代码实现它,才能够真正掌握它。你看电视剧里大侠在变NB之前没有那个是只看小人书;都是要天天苦练的。

要说明的是,这里学的都是数据结构的基础,不能满足所有算法的需求,常常需要对它们进行改造,比如说顺序表和树的私生子——线段树~充分证明了杂种优势理论的正确性。再比如后缀数组,再比如十字链表,再比如。。。(俺肚子里没存货了,大牛来补充点)。。。 这些特殊的数据结构在平常的学习中很少会接触到,这就需要多做acm/icpc的题目,不断积累。

另外,建议在实现某个数据结构的时候,用面向对象的思想来完成,写一个class,而不是用一堆联系松散的函数。(武大用的那本教程真ooxx)


--

当你终于能指使电脑mm去排队、种树、画图以后,就可以开始好好修炼算法了。

百度百科-> 算法 说:俺英文名是Algorithm,就是一系列解决问题的清晰指令,也就是说,能够对一定规范的输入,在有限时间内获得所要求的输出。

Orz...那最后几个字说得轻松阿,也不想想《算法导论》这块砖头有多厚。额滴神哪,Knuth还有几本《The Art of Computer Programming》好像好还没写完。。。先看这些东西吧。。。同样的,这里面不能学到所有的算法,还是需要多做题,积累。


--

最后,提一些icpc相关的东西

1. OJ, Online Judge
在线评测系统,知名的有vijos, usaco,poj, zoj等。提供一系列题目,允许浏览者提交相应的源代码并对其进行测试,是否能够完全符合题目要求。
以武汉大学在线评测系统woj为例,在
http://acm.whu.edu.cn/woj注册并登录以后可以试着打开
http://acm.whu.edu.cn/oak/problem/problem.jsp?problem_id=1001
这是最简单的题目,输入a, b,要求输出a与b的和(大多数OJ都有这一题,供测试使用)
(详细看看这个页面Hint部分的内容,有不了解的地方,阅读FAQ:
http://acm.whu.edu.cn/oak/faq.html)
点击页面底部的submit按钮,会转到有个大文本框的网页,把你的代码粘贴到文本框中,点击下面的submit按钮
然后浏览器就会把你的代码提交给服务器进行测试,并自动转到结果页面查看。
如果对应的结果是Accepted(简称AC),说明这道题你做对了,其他情况参见FAQ里的说明。

~一些OJ的链接
  
http://acm.timus.ru/(外国)
  
http://acm.hdu.edu.cn/杭州电子科技大学
  
http://acm.pku.edu.cn/JudgeOnline/北京大学
  
http://acm.nenu.edu.cn/或者http://acm.hrbeu.edu.cn/哈工程
  
http://acm.jlu.edu.cn/joj吉林大学
  
http://acm.tju.edu.cn/天津大学
  
http://acm.whu.edu.cn/武汉大学
  
http://acm.sgu.ru/俄罗斯圣萨拉托夫州大学在线题库
  
http://acm.mipt.ru/judge/bin/problems.pl?lang=en俄罗斯莫斯科物理技术学院
  
https://spoj.sphere.pl/波兰格但斯克理工大学
  
http://acm.uva.es/西班牙的Universidad de Valladolid在线题


2. 参见我之前写的这篇日志
http://www.felix021.com/blog/read.php?1318

   转自武汉大学felix大牛blog

你可能感兴趣的:(ACM之路)