Try   HackMD

競賽策略與鍛煉方向

競賽考驗如何活用有限的資源(時間、腦力等)來取得最大的效益。

除了知識與實作能力以外,策略以及心理狀態等各方面因素,也會大大地影響競賽的結果,其重要性不可忽視。

要在競賽取得好成績,必須連這些細節也要斤斤計較。

平常的練習是為了競賽能有更好的成績。

練習不像打怪練功,只要升級就一定變強。AC 越多題不見得能提升越多實力,因此不能只考慮練習時間以及完成的題數,必須重視從練習中能夠學到什麼、累積到什麼。

本篇以這兩點為大原則,來說明平時練習如何準備,以及賽中該怎麼應對。

閱讀所有題目

閱讀所有題目非常重要。不要急著開始寫第一題,並且絕對不要因為一題不會寫,就不往後看其它題目。

一般競賽會分成兩種:題目按難易度由簡而難排序,或者不按難易度排序。不管哪一種,這個原則都通用。

理由很簡單:

每個人對同一道題目的難度感受不一樣。

以 APCS 為例,客觀難度是 1<2<3<4,第 1 題最簡單,第 2 題實作困難但不用考慮計算效率,第 34 題則必須想出足夠有效率的解法,但實作相對不困難。

對於實作強,但缺乏計算效率的概念、或想法與知識不足的人,第 2 題不難,第 34 則很難取得高分;

對於想法強,但實作不夠安定的人,第 34 題可以很快想到怎麼做並寫出正確程式碼,第 2 題反而會寫起來非常卡。

甚至極端而言,想法強的人每題思考時間差不多都接近是 0,那麼實作相對花時間的第 2 題,反而會佔用最多時間,且最難保證正確無誤。

同理,假如有一題較簡單的實作題、一題較難的數學題,對於數學很強但實作不熟練的人而言,反而普遍認為較難的數學題反而更好解決。

配題策略

考慮以下問題:

給你正整數 N 代表賽中有多少題目,接下來 N 個正整數代表每題所需時間,最後給你正整數 M 代表比賽中有多少時間。求賽中最多可解出多少題?

這個問題的答案非常直覺,同時也是競賽的核心策略:

每次從未解出的題目中,找出所需時間最短者來解,直到時間不夠為止。

為此,你必須知道

  • 每一題在問什麼
  • 有能力從題目條件與所求,來估算 AC 所需時間

因此,必須先完整閱讀所有題目,才有得配題。

簡單、有把握的先做

簡單、有把握的定義:10 分鐘內可以 AC

如果存在這等級的題目,可以先做。不過在解題時間不存在罰分時,也可以看完所有題目再做。

不在這等級的題目,或者沒有足夠的把握,就算是確定會寫的,也應該先閱讀其它題目。

另外,即使你有把握,也可能只是題目看錯或估計錯誤,寫的時候必須計時,只要超出 15 分鐘沒有 AC,就馬上先去看別題,之後再回頭想。

子任務是關鍵

能在 10 分鐘內拿下的部份分,可以嘗試先拿

理想上,存在部份分的比賽,不應該有任何一題 0 分。能夠拿下的部份分必須把握住。

通常存在部份分,意味著可以用正確、但效率不足的做法去拿;或者進行特定限制時,會變成較單純的題目,有較針對性的解法。

品質高的題目,子任務會具備一定的引導性,可能幫助尋找通往正解的方向。

即使目標放在完整解出該題,子任務所含之線索仍然非常重要。

閱讀題目的細節

若沒有正確理解題意,實力再強也 AC 不了。

仔細、耐心地閱讀題目,注意每個條件以及數字範圍。這件事非常重要且基本,但重要性卻常被忽視。

有些重要的限制條件,會讓看似困難的問題變得相當容易。因此完整地閱讀非常重要。

有些題目被包裝得看似複雜難解,本質上只是簡單題。

儘管大半的問題,只看最後一段就能知道想問什麼,甚至存在靠範例測資來通靈題目所求的流派,不過不建議用這樣的方式練習。

請不要只看範例測資逆推,也不要只看題敘最後一段。

理由是有些題目重要條件會藏在題敘中,或者用較文章式的敘述來描寫。

特別是練習時,若認為閱讀煩躁而一昧貪快,長久下來會更沒耐心細讀文字、且訓練不到閱讀理解的能力,對成長不見得是好事。

練習估計解題所需時間

理想上,閱讀完一道題目,必須要在沒有其它提示的情形下,在最多 1 分鐘內粗略估算 AC 所需時間。

這仰賴於反覆的估算、計時、並且修正,才能趨於準確。儘管不可能百分百精準,但越準就越有參考價值。

在沒有情報的狀況下隨機選題,不去看 AC 率、難易指標、TAG 或題型等提示,閱讀完題目後,先進行估算,之後計時,AC 後進行比對,並回顧過程思考為何偏離,作為日後估計的參考。

講義的題單適合用來練習單一主題,但不適合拿來練習題型判斷與難易估計的部份;隨機選題在所學不多時容易踩到沒學過的東西或太難的題目,練習效益相對低,可是適合練習判斷力。

有前提情報時,對思考部份的估計較不準確,但依然可以用於估計規劃與實作部份所需時間。

計時和回顧也能協助了解解題過程中,每一步所花時間,以供檢討改進使用。

模擬賽的實際演練

打模擬賽非常重要。

實際去打模擬賽,會更了解賽中所面對的情況,包括時間限制所帶來的壓力,以及資源有限時的調度困難。

請避免拿正式的重要比賽作為人生初次參賽來累積經驗,畢竟機會有限,事前能累積就盡量累積,會更有利。

AtCoder 和 CodeForces 都有提供模擬參與過往比賽的功能,vjudge.net 可在支援的 OJ 中任選題目、自由設定時限等來舉辦模擬賽。即使系統不支援,也能自行計時並參考上傳記錄做到類似的事。

卡題時該做什麼

原則上,一道題目 5 分鐘沒有任何進展,或 15 分鐘看似有進展但沒多大變化,就該先停下來了。

嚴禁在同一題上連續卡住太久。

人在思考時,特別是不順遂時,容易鑽進死胡同打轉無法脫身,一兩題陷進泥沼可能比賽就這麼結束了。

要換別題,心理上又覺得已經花很多時間了,不捨得放棄或不甘心不肯認輸。

請努力克服。這些都是陷阱,容易成為敗因。

很多時候,先跳脫當前思考,想想別道題目再回頭,會有奇效。

勇於暴搜和暴力

不要因為測資範圍大就退縮,暴搜和暴力雖然算得慢,但能正確算出答案。

就算拿部份分也好,大部份對但少數情況會錯的假解衝衝看也好。

若賽制有子任務,應盡量爭取部份分數。

騙分導論

騙分導論

不得不說這很創意,也很實用。雖不建議只學些較邪門外道的做法,紮實的基本功更加重要;但許多時候不擇手段也要爭取分數的姿態是正面且有益的。

訓練時的心態

練習的最終目的是最終在競賽等狀況,需要用到平時累積的熟練與經驗的關鍵時刻,能夠有較良好的表現。

這並非學得更多、寫過更多題目就一定比較好,平常的小習慣也會影響長期累積的成果。

不要過於在意題數

題數與實力並不相等。

題數以及(OJ上的)排名雖能提供成就感與動力,同時追求題數也可能帶來不利的影響。例如傾向多寫熟練所以省時的題目、而不傾向寫不熟所以費時的題目,反而阻礙成長。

題數(或者作業)要灌水蒙混過去的方法多得是,不過這樣只是浪費時間而沒有獲得成長。

寫題單時要有目的性

練習題(或自己找來的特定題型題單)就算有多種解法,應盡量使用目的的方法去解。

例如放在練習整數與 if 的題目,雖然可能用字串更好解決,但就沒練習到主要想練習的東西。

多想不同解法絕對是好事,但因此沒練習到需要練的東西、或沒想到怎麼用這主題教的東西去解,那其實是一種浪費。

多花時間養成思考習慣

賽中能少想的事就該少想;練習時應多思考,不要想抄捷徑。

實作好好摸熟、想通每個細節,不要貪快或瞎猜。寫錯時要去弄懂原因和細節。

自己思考解法至少約 15 分以上,不要太早問人或看解答。

保持不滿於當前做法。隨時回顧解題過程,檢討並思考如何改進。

不盲目地反覆練習。很多時候與其相信熟能生巧,不如從做法的根本去改進、尋找更好的解決方法。

找出一套屬於自己的思考與驗證方法。

為了能偷懶而多思考,但不因偷懶而減少思考。

多嘗試不同的做法

賽中盡量用最熟悉有把握的方式;練習盡量多用不熟悉的做法,或同一題嘗試不同做法。

熟練的做法最不易出錯,但練習時一直傾向用熟悉的做法,會導致熟悉的東西不夠多、不夠廣。若賽中遇上熟悉的方式行不通、或不那麼適當時,應對能力會不足。

同一題多用不同方式多 AC 幾次,看似題數沒有增加,不過會累積不同的經驗。

試著找出更快的做法

賽中應在合理計算量(複雜度)中,選擇實作較安定的做法;練習應多想是否存在更快、更簡潔的做法。

例如今天有方法 A 和方法 B,方法 A 明顯比較慢,可是比較好寫;方法 B 明顯比較快,但比較難寫。

一般會認為方法 B 更好,因為它能面對題目更嚴苛的測試資料大小和更緊的時限。

但是若這題方法 A 足以獲得 AC,那麼賽中方法 A 能用更少的資源獲得更多的效益,是比方法 B 更優的選擇。

練習時則應嘗試掌握方法 B 或甚至思考是否存在更快的其它方法,以應對更嚴苛的條件以及測資。一昧貪快選擇方法 A 可以較快獲得 AC 使題數增加,但賽中不幸遇到方法 A 不夠快的情況,就會陷入不利。

從錯誤中學習

向失敗學習,記取教訓並檢討如何避免失敗。

面對失敗,不要逃避或者無視。失敗能教給我們的東西,遠比成功多得太多。

不要白白的失敗卻沒得到任何成長,那是種浪費。記住那份痛楚與不甘,並轉成動力。

練習時多失敗是好事,總比重大競賽中失敗得好;只是有時摔不夠重,就學不乖。重大競賽中的失敗,對整體人生或許反而是件好事。

多看官方標準或其他人的做法

盡量避免在練習前看解答,或至少別太快放棄;AC 後則應多看別人的做法,或可以拿去問人是否有更好的做法。

特別是像 AtCoder 或 CodeForces 等 OJ 每題都有官方標準解答,建議多看,有時會有意外的收獲。

例如:從未想過的做法、特定技巧的奇妙運用方式、不同的切入問題與看待事物的角度、實作上的小技巧…等。

整理程式碼與邏輯

思考解法時細分不同狀況想清楚,規劃實作時嘗試整理邏輯,尋找或製造共通性、減少例外。

例如考慮正整數 N 是否為特定正整數 k 的倍數的情況,很直覺可能會想到若 N<k 那肯定不成立,因此可將狀況區分成單純的 N<k 和較複雜的 Nk 兩邊。

最後找到的判斷方式可能是判斷 N%k=0,再多想一下會發現其實 N<k 的話 N%k0 必定成立,所以用這個條件判斷時,根本沒必要區分 N 是否比 k 更小。

發想階段區分不同情形是會有幫助的,因此這個思考過程並沒有問題。

有意識的去整理結論,可能得到更漂亮的結果,將來用到相似結論時較有利,習慣整理結論也對思考能力有幫助。

要傳就要一次過

帶著「每題只有一次上傳機會」的覺悟去練習。

不應該將確保自己程式碼正確性的責任甩給 OJ。

該負責確保正確無誤的是作者,為此除了範例應自行追測各種可疑或不確定的狀況,待有信心自己能想到的狀況都能正確處理,才上傳做最後確認。

千萬不要抱持「雖然不知道對不對,總之先傳再說」「反正傳了就知道」的想法去練習。

這樣寫程式時很容易粗心、寫錯而沒有自覺,寫程式的習慣容易變糟。

對於後測(賽中無法得知最終結果)或存在罰分(上傳沒有 AC 會受到些許懲罰)的賽制會有相當大的負面影響。

不要逃避實作複雜繁瑣的題目

某程度上,夠麻煩且繁瑣的實作題,更能幫助訓練如何整理程式邏輯、寫出更簡潔且漂亮的程式碼。

或許練習這類題目,單論對競賽的效益並不高。不過對整體程式能力是不壞的訓練。

隨機尋找題目

嘗試在「不知道題目任何資訊」的情況解題,訓練判斷能力。

這裡的資訊包括題目粗略的難易度、AC 率與 AC 人數、TAG、題型、提示或其它情報。

這適合在至少把二章學完後、最好三章學完後再開始,才比較有面對大多數題目的基本知識。

隨著知識增加,能運用的技能增加,要在適當的場合找到適當的方法來運用,會因選項大量增加而相對變得困難。

思考無任何情報的未知問題,對於如何尋找解題方向、題目線索的判讀、難易度的判斷、所需時間的估計等,各方面都會有所成長。

如果只寫已知是特定主題的練習用題單,以上的判斷能力會相對低落。

放鬆和休息十分重要

賽前盡可能放鬆心情、獲得充分的休息,同時注意身體健康狀況。

休息不足或生病會導致失常,因此不建議賽前因不安而過度的特訓。練習是平常就該練的,賽前臨時抱佛腳意義不大,不如好好休息放鬆。

調整身心狀況在最佳狀態去面對競賽,是一件非常重要、卻意外的容易被忽視的事。

小結

練習以獲取經驗、教訓、各種熟練度為主,為了賽中取得多一點的有利,該花的時間心力不能省。

賽中需活用有限的資源來取得最大效益,透過模擬賽與賽後檢討能更明白賽中需要什麼、如何準備。

以此為基準,真摯地面對每個課題,在平時做好準備、賽前注意照顧身心健康。

tags: 競程:初章後半, 競程