ACM-ICPC世界冠軍教你如何備戰程式設計競賽

徐騫發表於2013-07-15
這是ACM-ICPC 2011世界冠軍巫澤俊對他最近翻譯的《挑戰程式設計競賽(第2版)》的感受以及對備戰時演算法相關書籍的點評。如果你對程式設計競賽有興趣,不妨看一看。

前幾天,秋葉拓哉(iwi)、巖田陽一(wata)和北川宜稔(kita_masa)所著,我(watashi)、莊俊元(navi)和李津羽(itsuhane)翻譯的《挑戰程式設計競賽》,終於通過人民郵電出版社正式出版了。可喜可賀,可喜可賀。有關該書的簡介、目錄、試讀和購買連結請訪問這裡。這裡我主要想說一下自己為什麼要翻譯和推薦本書,還有對程式設計競賽學習資料的一些看法。也附帶一些對譯者序和第1章的補充說明。

在譯者序中我也略談到了自己翻譯此書的動機。和很多讀者一樣,最開始當然是奔著作者的名頭去的,三位作者不但是國際知名的選手,而且TopCoder的最高rating加起來都破9k了。頂尖實力的作者往往可以站在更高的高度指點江山,也就更可能寫出一本好書。隨後就是讀到書中的內容了,書中的絕大多數東西,我大概都在過去四五年時間的摸爬滾打中,逐步通過各種書籍、網路、道聽途說、解題經驗和總結體會掌握了。不過還是有一些耳目一新的內容,其中有一兩個問題還是通過郵件得到了原作者的解答,張了知識。但仍有一種相見恨晚的感覺,假如自己早兩年讀到此書,想必能少費不少勁。從我個人的經歷和對周圍同學的瞭解來看,這是一本非常值得引進和推薦的書。

當然,在此之前國內已經出版有不少演算法競賽相關的書籍了,很多人想必希望知道這本書有什麼特別之處。演算法競賽相關的書籍大致有兩類,一類是演算法和數學類的書籍,比如各種資料結構教材、離散數學教材、《演算法導論》、《具體數學》等,一類是專門針對演算法競賽的書籍,其中的代表就是劉汝佳所著的幾本書,而《演算法藝術與資訊學競賽》(黑書)又是其中的代表。作者之一的iwi在MSRA實習期間也得知了黑書的大名。

首先,個人覺得這些書籍大致可以分為兩類:教科書和工具書。諸如《資料結構與演算法分析》(DSAA)之類的書可以作為教科書的代表,而諸如《計算機程式設計藝術》(大名鼎鼎的TAOCP)則毫無疑問是工具書的代表。大致地說,前者簡單易懂,適於學習,後者高深全面,適於參考。二者並沒有明顯的分界線,很多時候全憑主觀,因人而異。比如說,看懂了,這就是教科書,看懂目錄了,這就是工具書。當然,和數學沾邊越多的書,總是越難啃的,所以就難度而言,這類書籍和程式語言類書籍自然是沒有可比性。

許多書都作為程式設計競賽的學習資料被反覆推薦,但事實上,我們大概可以仿照《最常被程式設計師們謊稱讀過的計算機書籍》寫一篇《最常被ACMer們謊稱讀過的書籍》的吐槽文。裡頭有一句話很重要,所以我再抄一遍:“如果你自己沒有讀過這些計算機書籍,請不要推薦給別人”。當然,像《演算法導論》這樣的書個人覺得還是值得一讀的,多數的章節並不難,可以當作教科書,後面的一些內容可以作為工具書需要時再參考,裡面很多東西講得很細,容易做到真正的理解吸收,比如從自動機引出KMP,等等。而TAOCP則無疑是最常躺槍的裝逼神器。有一天,我在同學的桌上看到一本TAOCP第一卷,開啟一看很黃很暴力,我趕緊就把它蓋上了。TAOCP很厲害,看exponentiation by squaring能引用到它,看permanent也能引用到它,連看數え上げお姉さん都能引用到它。讀完TAOCP那必須能變得超厲害了,可那得是能讀完啊,讀不完說啥都白搭。所以推薦學習資料不能光看書好不好,還得看對目標人群合不合適。而一本好的教科書,不應該是儘可能體現作者有多牛,而是要能夠儘可能簡單地幫助讀者變得更牛。如果看完了,懂的還是懂,不懂的還是不懂,那是沒有意義的。

按照這個分類標準,個人覺得《挑戰程式設計競賽》是一本很好的教科書。它非常適合作為有志參加程式設計競賽的同學自學,或是正在學習資料結構與演算法分析的讀者作為練習和擴充的參考書。該書將不同的主題按難度分成了三部分,循序漸進。作為教科書,其一個明顯的特點是,幾乎沒有外鏈,把每個的主題都講得很清楚,便於讀者理解。多數題目附有核心程式碼,程式碼風格也不錯,而且講解的時候會附帶一些思路的說明和方法技巧的總結。它在正文中詳細完整的介紹了在程式設計競賽中最重要的知識和思想,全書涵蓋了在絕大多數題目中所會用到的知識和思想。而對於擴充內容則以補充說明或是附錄的形式給出,並未多做介紹。這樣的好處是該書很連貫,結合練習容易完整掌握,並突出了對大多數人而言更為重要,應該多花力氣的地方。

在此舉幾個例子。書中介紹一般圖匹配的時候並沒有介紹經典的帶花樹實現,而是介紹了利用Tutte矩陣求匹配數的隨機演算法。因為要在書中真正把帶花樹的實現給讀者講清楚很困難,而對Edmonds演算法的介紹資料也很多。在介紹字尾陣列的時候,用的也不是線性演算法,因為字尾陣列最重要的是理解其性質和應用,而求字尾陣列往往是模板的工作。在介紹字串上的動態規劃演算法的時候,沒有介紹KMP演算法和Aho-Corasick演算法,而是用暴力的方法求出了二者所對應的狀態和轉移,事實上這反而更有助於真正理解KMP演算法和Aho-Corasick演算法。

另一方面,如果你是想要學像弦圖、動態樹、Dancing Links(DLX)之類的高深玩意的話,這本書對你就幾乎沒有任何幫助。當然,說到DLX,我不知道為什麼國內會曾經有一段時間盛行出DLX的題,題目的規模大得驚人,能AC的原因卻是因為資料太水。真正理解DLX的人應該明白,這本質就是一個常數優化的啟發式搜尋,並不能改變問題本身的困難程度。從某種意義上說,在把基礎搞紮實前,還是少折騰這些高深玩意比較靠譜。

相比較而言,劉汝佳的《演算法藝術與資訊學競賽》則介於教科書與工具書之間。書中按專題介紹了了相當多的知識,從頭到尾許多小節都涉及到非常難的內容,還有一些神題。書中所提及的演算法和資料結構要比《挑戰程式設計競賽》多得多,特別是計算幾何部分要更為系統詳細。書中包含有大量的外鏈,非常可惜的是OIBH早在多年前就掛掉了。書中對很多問題進行了總結歸納,但不都有詳細的講解介紹,對於身經百戰的讀者很容易找到共鳴,但對於其他讀者而言,恐怕讀起來就不那麼輕鬆了,很多地方需要自己鑽研,另找資料學習。黑書是可以多讀幾遍的書。起初可以在短時間內接觸大量的演算法和技巧,然後通過其他資料學習。遇到某個有印象的問題,或時隔一段時間後,可以再翻開看。慢慢地,就會對書中越來越多東西有共鳴了。

當然,從最初閱讀《演算法藝術與資訊學競賽》,到去年翻譯《挑戰程式設計競賽》,我自身的實力也已經不可同日而語了。所以上面對於兩書的評價,當作兩個不同的人做出的應該會更為客觀一些。

冰凍三尺非一日之寒。雖然書中的內容已經足以讓讀者的rating衝上2500,但真要達到2500的實力卻還離不開充足的訓練,通過實踐把書中的內容真正化為己有。另一方面,訓練的方法也是很重要了,好的方法能夠做到事半功倍。正如《挑戰程式設計競賽》最開始所說的:程式設計競賽是綜合了以下兩個要素的複合競賽:

  • 設計高效且正確的演算法
  • 正確地實現

並且,為了設計演算法:

  • 靈活的想象力
  • 演算法的基礎知識

也是必不可少的。平時訓練過程中切莫顧此失彼。

Q&A

Q: 能找作者/譯者簽名麼?  
A: 如果我們有機會見面的話,而且你居然還不嫌我的字醜的話,是可以的。由於我和另外兩位譯者馬上就要分處三個不同的城市了,能不能集齊就得看RP了。如果你參加今年的TCO的話,我們到時候還可以去搭訕iwi和wata要簽名。  

Q: 賣出一本書你能抽成多少?  
A: 0。 我們拿的是一筆稿酬。所以如果你買書的目標是給我錢的話,不如直接一點,開啟支付寶。我會很開心的。  

Q: 為什麼黑Knuth?  
A: 呃,雖然文中黑的兩個東西全是Knuth搞的,但我絕無黑Knuth的意思,恰恰相反,我黑的是那些褻瀆Knuth作品的人。比如建議你想要進某社先把TAOCP讀個幾遍啊,或者把DLX當萬金油使的這類人。 

Q: 《具體數學》怎麼樣?  
A: 說來這又是Knuth搞的東西?《具體數學》可以算不怎麼簡單的教科書。裡面許多內容還是可以看得津津有味的,但是有些地方就看著有點費勁了,裡面那兩頁求和公式“簡單的例子”當初可看死我了。《具體數學》系統地介紹了許多有用的數學工具,當你遇到過幾類數學相關的問題,並開始覺得自己不會數數,不會算概率的時候,就很有看一看的必要了。

Q: 有更上一層樓的練習方法嗎?
A: 《挑戰程式設計競賽》也有這麼一段,這裡做一個補充。一是近年來Codeforces得到了很大的發展,其功能豐富,使用者群也很大,是一個很好的練習平臺。二是SGU也是一個老牌的OJ,其題庫比較精簡,且質量比較高,不會像其它OJ有刷不完的題,因此比較推薦。學習演算法的時候,可以找一些相關的題做,鞏固知識。練習的時候,不應該跟著解題報告做題,而是要儘量獨立思考,否則“靈活的想象力“恐難有提高。同樣的,不要在“神模板”和“神題”上走火入魔,而忽略了“演算法的基礎知識”。 

Q: 我想問下: 有人說,奮鬥的意義不在於結果,而在於過程……你在ACM的過程中收穫了什麼呢?如果當初給你不搞ACM,會做什麼呢?   
A: 其實主要的收穫在兩方面:  
   1. 通過大量的演算法題訓練,鍛鍊了邏輯思維和寫出漂亮程式碼片段的能力  
   2. 結識了不少優秀的浙大學長和外校同好  
   當然,藉著各種onsite比賽的機會,也增長了不少見識。  
   至於不搞ACM會做什麼,那就說不準了,太yy了…… 

Q:對於大三即將大四畢業的,並且以後從事IT程式設計方面的工作,這本書對於以後的職業生涯有什麼幫助?
A:對絕大多數參與工程開發的程式設計師而言,學習演算法恐怕沒有那麼明顯和直觀的幫助
大多數領域並不會涉及多少這類演算法的設計,只要具備選擇合適的資料結構的能力就足夠了。不過通過練習編寫解決複雜演算法問題的程式碼,對鍛鍊思維和寫出漂亮的程式碼還是有幫助的。所以個人有興趣的話,可以鑽研一下。但是沒有興趣,純以為未來工作準備的話,視工作應該有很多更合適的書。類似的,熟練掌握演算法在面試中應該會佔點便宜,但真要比較紮實的掌握,時間上的投入是少不的。而純粹以面試為目的,又有另一些(我不太喜歡,乃至比較反感的)捷徑。

Q: ….  
A: 如有其它相關問題,歡迎留言。

本文轉自《挑戰程式設計競賽》譯者巫澤俊的blog:http://blog.watashi.ws/2382/pccb-etc/

相關文章