入門與避坑指南)
先說明一下“3.3 OJ”大概率不是你一個人要面對的東西。很多學(xué)校的數(shù)據(jù)結(jié)構(gòu)、C語言、算法設(shè)計課上老師會把作業(yè)掛在“Online Judge”在線評測系統(tǒng)簡稱OJ里3.3就是某個章節(jié)的第三題。我第一次在江南OJ上看到這種格式的題目時也想了半天這到底是要干嘛——交個代碼還分章節(jié)后來才明白這是課程練習(xí)的自然編號第3講、第3題。這篇文章就寫給正在被“3.3 OJ”這類題折磨的同學(xué)。我會把OJ到底是什么、做題流程、常見報錯、輸入輸出坑、還有那個所有人都想問但不好意思問的“搜OJ答案到底行不行”一次講清楚。你不需要有很強的算法底子只要會寫最基礎(chǔ)的hello world就能看懂。1. 先弄明白OJ到底在考什么1.1 OJ不是“寫代碼”是“寫程序”很多同學(xué)第一次交OJ題覺得自己代碼邏輯完全正確結(jié)果返回來一個Wrong Answer當(dāng)場蒙了。這里有個核心認(rèn)知必須建立起來OJ不是靠人工看你的代碼好不好、規(guī)不規(guī)范它根本不會“看”你的代碼它只看你的程序運行結(jié)果對不對。整個評測流程是這樣的你提交一段源代碼OJ服務(wù)器把它編譯成可執(zhí)行文件然后把預(yù)設(shè)好的測試數(shù)據(jù)作為輸入喂給你的程序你的程序運行后產(chǎn)生的輸出被捕獲下來最后和標(biāo)準(zhǔn)答案做逐字節(jié)的比較。全部數(shù)據(jù)一致判ACAccepted通過有一個字符不一樣判WAWrong Answer答案錯誤運行超時判TLETime Limit Exceeded超時非法訪問內(nèi)存判RERuntime Error運行時錯誤。這個過程特別像考試時用的答題卡掃描機。掃描機不會管你寫字好看難看只看你涂的格子對不對。OJ也是這樣它不管你的代碼用了什么奇技淫巧也不管你的縮進是不是標(biāo)準(zhǔn)你的程序?qū)y試數(shù)據(jù)給出的輸出和標(biāo)準(zhǔn)答案完全一致就給你過。這種“只看結(jié)果不看過程”的評判方式讓很多第一次接觸的人不習(xí)慣。我們平時寫代碼會有IDE的提示會有斷點調(diào)試會有print大法看中間值這些東西在OJ上統(tǒng)統(tǒng)不存在。你提交上去的是一坨黑盒能看到的只有那個最終狀態(tài)。所以準(zhǔn)備OJ題的第一個思維轉(zhuǎn)變就是你寫的不是“代碼”是“程序”是一個從標(biāo)準(zhǔn)輸入讀取數(shù)據(jù)、向標(biāo)準(zhǔn)輸出打印結(jié)果的獨立程序。這意味著你在本地寫的那些帶圖形界面、帶鼠標(biāo)點擊的代碼在OJ上根本跑不了你在代碼里寫死的測試數(shù)據(jù)OJ也不會理你寫的那些讓用戶輸入數(shù)字的printf提示語反而會變成輸出的一部分導(dǎo)致WA。在OJ的坐標(biāo)系里程序就是“輸入 - 處理 - 輸出”這三段論多出來的都是累贅。1.2 為什么幾乎所有高校都有自己的OJ你可能見過江南OJ、杭師大OJ、鄭州輕工業(yè)大學(xué)OJ、湘潭大學(xué)OJ、東方博宜OJ這些名字還會奇怪為什么每個學(xué)校都要自己搞一套直接用LeetCode、牛客網(wǎng)不行嗎。這個事得從兩方面看。歷史原因是ACM/ICPC程序設(shè)計競賽。從上世紀(jì)開始高校的ACM集訓(xùn)隊就需要一個能自動判題的內(nèi)部系統(tǒng)來訓(xùn)練隊員各校的技術(shù)團隊就陸續(xù)自建了OJ。后來程序設(shè)計課的老師發(fā)現(xiàn)這玩意太好用了作業(yè)不用手動改學(xué)生交完系統(tǒng)自動判分成績直接進數(shù)據(jù)庫查重也方便。于是OJ就從競賽專用工具變成了課程標(biāo)配?,F(xiàn)實里這些OJ還承擔(dān)著一個重要角色題庫就是打包好的實驗手冊。像鄭州輕工業(yè)大學(xué)的OJ、湘潭大學(xué)的OJ題目編號往往和課程章節(jié)是對應(yīng)的。老師把課后練習(xí)掛在上面題目難度遞進章節(jié)清晰“3.3”這種編號在課程體系里是第3章的配套練習(xí)做完這些題你的章節(jié)知識就算過了第一遍。這個設(shè)計和LeetCode那種海量題庫不一樣高校OJ的題目更多是“夠用就好”緊貼課程內(nèi)容的。還有一類值得留意的是華為OJ。一些大廠在技術(shù)校招筆試階段會搭建自己的在線筆試系統(tǒng)題型和OJ一致你說它為了篩什么篩的不僅是算法能力還有你讀題、處理邊界、在有限時間內(nèi)交付一個可運行程序的能力。所以大學(xué)期間把學(xué)校OJ上那些題刷明白到筆試階段會順手很多。1.3 “3.3”這個編號背后隱藏的課程線索看到“3.3”別只把它當(dāng)一道題。它通常暗示你已經(jīng)學(xué)到了某個課程的第三章。我自己的經(jīng)驗是第三章往往是一個分水嶺。以C語言課為例第一章是printf、變量第二章是if和循環(huán)第三章大概率是數(shù)組或者函數(shù)。到了數(shù)組這一章OJ題的難度會突然上一個小臺階因為題目的輸入不再是單個值而是一串值你需要處理的邏輯不再是“判斷一次”而是“批量判斷”。另一種情況是“3.3”是某本教材或者某個OJ題庫里的絕對編號。這種情況下你需要自己去題庫里找對應(yīng)的題號點進去看題目描述。判斷到底是哪種編號的辦法很簡單看有沒有給你一個可以直接跳轉(zhuǎn)的題號或者看班級群老師有沒有發(fā)題目列表。別小看這一步很多人卡在“找不到題”上其實不是題找不到是你沒有確認(rèn)這個編號的規(guī)則。2. 拿到一道OJ題先別急著敲代碼2.1 把題目描述拆成三塊我說句可能不太中聽的話大部分OJ題做不出來不是代碼能力不行是題目沒讀明白。OJ的題目描述通常有固定的三段式輸入格式、輸出格式、樣例。在做題之前先把這三塊用筆畫出來一個一個對照著理解。輸入格式告訴你程序要讀什么。有一個整數(shù)就定義一個整數(shù)變量接收有一行空格分隔的整數(shù)就循環(huán)去讀或許先給一個T表示測試組數(shù)后面跟著T組數(shù)據(jù)。這些信息直接決定你代碼里怎么處理輸入流。輸出格式告訴你程序要輸出什么。是輸出一個整數(shù)還是保留兩位小數(shù)是一行一個結(jié)果還是所有結(jié)果在一行這些決定你printf或者cout的格式串怎么寫。樣例是一個“最小化的黃金組合”展示一組輸入對應(yīng)的完整輸出。注意樣例的目的是讓你檢驗自己的理解對不對它的覆蓋范圍極其有限真正決定你生死的測試數(shù)據(jù)比樣例復(fù)雜非常多后面我會專門講。我見過不少同學(xué)拿到題目先把樣例復(fù)制到本地然后程序里對樣例數(shù)據(jù)硬編碼寫一堆if判斷如果是1就輸出1如果是2就輸出2樣例過了就提交。結(jié)果當(dāng)然是WA因為OJ的測試數(shù)據(jù)根本不是你猜的那個。這種“面向樣例編程”的做法除了自我感動沒有任何意義。2.2 數(shù)據(jù)范圍永遠(yuǎn)決定算法選擇題目描述里有一行小字最容易被忽略但它可能是整道題里最重要的一句話數(shù)據(jù)范圍。比如“1 ≤ n ≤ 1000”和“1 ≤ n ≤ 10^5”看上去只是數(shù)字大小不一樣實際上對應(yīng)的算法難度完全不同。n ≤ 1000時寫個雙重循環(huán)的O(n2)算法算一下最壞情況1000×1000等于100萬次操作現(xiàn)代CPU大概幾毫秒跑完沒問題。n ≤ 10^5時n2就是100億次操作妥妥的超時。這時你需要往O(nlogn)甚至O(n)的方向想比如排序加掃一遍、雙指針、二分、哈希。這些復(fù)雜度概念是OJ題的核心分水嶺。怎么判斷自己的算法能不能過有一個粗略的經(jīng)驗值大部分OJ的時間限制是1秒1秒內(nèi)大概能執(zhí)行10^8次簡單運算。你把n代入自己的算法復(fù)雜度算出來的操作數(shù)超過10^8基本就要優(yōu)化了。比如n10000O(n2)是1億次很懸n10^5O(n2)就是10^10次絕對TLE。每次交題之前先算這個賬能幫你省下大量試錯成本。2.3 樣例只是“示意”不是“全部”很多WA的根源是對樣例產(chǎn)生了錯誤的信任。樣例過了就覺得自己程序是對的直接提交返回來一個WA然后就蒙了——到底哪錯了構(gòu)造邊界測試是刷OJ的第一項基本功。題目說n≥1你就測n1說數(shù)組長度最多1000你就測1000說有正有負(fù)你就測全是負(fù)數(shù)的情況說字符串可能有空格你就測帶空格的字符串。把這些邊界情況在本地都測一遍很多時候WA的原因自己就能揪出來。進階一點的做法是“反推測試數(shù)據(jù)”。你看到樣例輸入是“1\n2”輸出是“3”這是加法題。但OJ的實際測試數(shù)據(jù)可能包含“負(fù)數(shù)加正數(shù)”、“大數(shù)加小數(shù)”、“0加0”。你需要在提交前把每一條可能的輸入分支在腦子里過一遍并實際在本地跑這些分支。不是每個OJ都告訴你“評測失敗的第一個測試點是什么”所以自測能力的重要性怎么強調(diào)都不為過。3. 常見WA、RE、TLE背后的真實原因與排查3.1 WA也就是答案錯誤幾乎所有新手遇到最多的狀態(tài)WA是整個OJ評測里信息量最少、但也最常見的返回結(jié)果。它只告訴你“你的程序和標(biāo)準(zhǔn)答案不一致”但完全不告訴你測試數(shù)據(jù)是什么。排查它的思路應(yīng)該是系統(tǒng)的不是靠猜。第一查輸出格式??纯茨┪彩遣皇嵌嗔艘粋€空格、少了一個換行百分比符號后面是不是該保留兩位小數(shù)你寫成了整數(shù)。OJ的輸出對比是逐字符的多一個空格都是WA。第二查邏輯邊界。循環(huán)是不是多跑了一次數(shù)組下標(biāo)是不是從1開始但你聲明的是從0開始判斷條件里用了大于等于還是大于這類問題靠讀代碼很難發(fā)現(xiàn)靠構(gòu)造測試數(shù)據(jù)一測就出來。第三查算法本身。你的思路可能整體方向沒問題但某個細(xì)節(jié)漏了。比如求最大公約數(shù)時沒考慮負(fù)數(shù)排序時沒考慮穩(wěn)定性的要求。第四查數(shù)據(jù)類型。這是非常容易忽略的坑。題目說n≤10^9你用int接收10^9在int范圍內(nèi)int最大約2.1×10^9但如果兩個10^9相加int就溢出了。凡是涉及乘法、累加、差值的先估算一下結(jié)果的最大可能值超了就換成long long。這個簡單的習(xí)慣能把一半的WA消滅在萌芽期。3.2 RE也就是運行時錯誤多半是程序直接崩了RE意味著程序在運行中途異常終止。常見的元兇是數(shù)組越界、除數(shù)為0、棧溢出、空指針。數(shù)組越界是最常見的。你開了int a[100]但循環(huán)里寫了for (int i0; i100; i)最后一次訪問a[100]就越界了。有些本地環(huán)境對越界訪問不報錯因為那片內(nèi)存碰巧是可訪問的值還是“碰巧對”的但OJ的環(huán)境更嚴(yán)格直接RE。解決思路是凡是數(shù)組統(tǒng)一把上界放寬一點。需要n個元素就開n5個多出來的空間留著當(dāng)緩沖這是競賽圈通行的做法。除0錯誤發(fā)生在輸入數(shù)據(jù)里恰好出現(xiàn)0而你的分母變量沒做判斷的時候。解決思路是在除法前永遠(yuǎn)檢查分母。棧溢出在遞歸代碼里常見比如遞歸深度到10^5層函數(shù)調(diào)用棧就爆了。C的局部變量默認(rèn)放在棧上一個深度很深的遞歸每次調(diào)用都開辟棧幀撐爆了就是RE。如果是遞歸爆??梢钥紤]改成迭代或者把遞歸里的大數(shù)組改成全局變量。排查RE的方法很笨但有效把代碼里的關(guān)鍵數(shù)組大小改小先跑小數(shù)據(jù)看能不能穩(wěn)定復(fù)現(xiàn)然后把輸入數(shù)據(jù)打印出來手動模擬一次看程序走到哪一步崩的。代碼規(guī)模不大時這個排查過程用不了十分鐘。3.3 TLE也就是超時你的程序跑得太慢了TLE不是“答案不對”是“答案來不及算完”。這是最需要認(rèn)真對待的判題結(jié)果因為往往意味著你需要優(yōu)化算法而不是修修補補。第一個排查點是算法復(fù)雜度。我前面說過1秒大約能跑10^8次運算。如果你的代碼是O(n2)n是10^5那肯定超時。這時候的方向是換個復(fù)雜度更低的算法比如把冒泡排序換成sort把一個一個比較換成二分查找。第二個排查點是輸入輸出效率。這是很多Java和Python選手踩過的坑。Scanner的nextInt在處理超大量輸入時極慢BufferedReader.readLine加split會快很多Python的input()在大數(shù)據(jù)下也是個瓶頸可以換成sys.stdin.buffer.read。C的cin如果不關(guān)同步也會比scanf慢。少數(shù)TLE其實不是因為算法不行而是讀數(shù)據(jù)太慢。第三個排查點是常數(shù)優(yōu)化。同樣復(fù)雜度人家能過你過不了可能就是代碼里多了些重復(fù)計算。循環(huán)里反復(fù)調(diào)用了某個函數(shù)、每次循環(huán)都重新分配了數(shù)組、字符串拼接用了而不是join這些細(xì)節(jié)累積起來也是可觀的耗時。說實話TLE是所有返回結(jié)果里最能逼人成長的。當(dāng)你第一次因為超時而被迫學(xué)習(xí)前綴和、二分查找、雙指針這些優(yōu)化技巧時你對算法的理解才算真正開始了。3.4 PE也就是格式錯誤差一點就對了PE在大多數(shù)OJ里是介于WA和AC之間的狀態(tài)表示你的輸出內(nèi)容基本正確但格式?jīng)]有完全匹配多了個空格少了個換行或者行末多了個空字符串。它像是一個善意的提醒“你離正確答案就差一個空格了”。處理PE的思路很簡單嚴(yán)格按照題目給的輸出格式逐字符對齊。樣例里如果輸出是“Case 1: 3”冒號后面有一個空格你輸出“Case 1:3”內(nèi)容對但格式不對。這種細(xì)節(jié)是對細(xì)心程度的直接考察也往往是競賽選手和普通選手的分水嶺之一。4. 輸入輸出的那些“坑”與標(biāo)準(zhǔn)化模板4.1 你必須搞清楚的三種輸入模式OJ的輸入模式看起來千變?nèi)f化實際只有三種。每種模式對應(yīng)的代碼模板相對固定練熟之后遇到任何題都能快速套用。第一種是單組輸入。程序只讀一次數(shù)據(jù)處理完就結(jié)束。比如“輸入兩個整數(shù)a和b輸出它們的和”。代碼最簡單的形式就是讀一次、輸出一次。第二種是固定組數(shù)輸入。先讀一個整數(shù)T表示后面有T組測試數(shù)據(jù)然后循環(huán)T次每次處理一組。這種模式在入門題里很常見。模板大概是讀Tfor循環(huán)T次每次讀對應(yīng)的數(shù)據(jù)并輸出結(jié)果。第三種是未知組數(shù)輸入一直讀到文件末尾EOF。這種最常見也最折磨新手。C語言的寫法是while (scanf(%d, n) ! EOF)C的寫法是while (cin n)Python則是用for line in sys.stdin去遍歷。你需要做的第一件事是判斷題目屬于哪種模式。怎么看看輸入格式的描述如果里面寫了“輸入數(shù)據(jù)有多組每組占一行處理到文件結(jié)束”那就是第三種如果第一行就說了T那多半是第二種??辞宄賱邮帜苌僮吆芏鄰澛?。4.2 各語言的輸入輸出模板以C為例最常見也最直接的輸入輸出方式就是cin和cout。但有幾個點需要留意。如果你用的是scanf和printf格式串要寫對整型是%d長整型是%lld浮點數(shù)是%f或%lf字符是%c字符串是%s。如果數(shù)據(jù)量大或者你經(jīng)驗豐富可以在main函數(shù)開頭加一行ios_base::sync_with_stdio(false);來關(guān)閉cin和stdio的同步這樣cin讀入會快很多在很多OJ上面能顯著降低因為IO導(dǎo)致的TLE風(fēng)險。Python的寫法要注意比賽環(huán)境里input()雖然直觀但面對百萬級別行的輸入會非常吃力。更穩(wěn)的模板是import sys data sys.stdin.buffer.read().split() # 之后從data里按順序取數(shù)據(jù)這個讀法一次性讀入全部輸入再split速度比一行行input()快一個數(shù)量級。Java選手優(yōu)先用BufferedReaderBufferedReader br new BufferedReader(new InputStreamReader(System.in)); StringTokenizer st new StringTokenizer(br.readLine()); int t Integer.parseInt(st.nextToken());4.3 格式化輸出的三個高頻細(xì)節(jié)輸出整數(shù)直接printf(%d, x)或者cout x。輸出浮點數(shù)時題目如果要求“保留兩位小數(shù)”C選手可以用printf(%.2f, x)Python用print(f{x:.2f})Java用System.out.printf(%.2f, x)。注意四舍五入的規(guī)則在不同語言里可能有細(xì)微差別但OJ的標(biāo)準(zhǔn)答案通常按四舍五入處理。多行輸出很多題目要求每組答案占一行。寫代碼時記得在每次循環(huán)輸出的末尾加換行符。最后一行多一個換行符通常不算錯。但反過來少了一個換行符可能WA。所以收尾的時候統(tǒng)一加\n是比較穩(wěn)妥的習(xí)慣。還有一種常見坑行末不能有多余空格。有些題要求輸出一個數(shù)組的所有元素元素間用空格分隔但最后一個元素后面不能有空格。這時候很推薦用一個變量控制分隔符的打印for (int i 0; i n; i) { if (i 0) cout ; cout a[i]; }這樣輸出出來是“1 2 3”不會多一個尾隨空格簡潔又安全。4.4 提交前必須刪掉的“本地調(diào)試代碼”這個坑我見得太多了。本地寫代碼的時候你可能加了這些內(nèi)容提示語“請輸入一個數(shù)字”、自己打印的中間變量、注釋掉的代碼塊、或者用了文件讀寫的重定向語句。提交之前這些都必須清理干凈。為什么因為OJ在評測的時候會把你的標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)答案逐字對比。你多打印一句“please input”多打印一個調(diào)試用的臨時值都會讓你本來正確的計算邏輯變成WA。很多同學(xué)第一次提交WA之后跑回來問老師“我本地明明是對的”自己去代碼里一看十條調(diào)試輸出還在刪掉就好了就是這種事。如果你用了文件重定向比如C語言的freopen(in.txt, r, stdin)本地調(diào)試確實方便但提交到OJ上OJ評測機并沒有in.txt這個文件程序就會讀不到輸入直接報錯或者讀到錯誤內(nèi)容。提交之前把這些語句注釋掉或刪除是最基本的習(xí)慣。5. 關(guān)于“搜OJ答案”這件事我的真實看法5.1 為什么你搜到的答案經(jīng)?!芭懿贿^”我知道很多同學(xué)在面對“3.3 OJ”這種題的第一反應(yīng)是搜“XX大學(xué)OJ答案”比如搜鄭州輕工業(yè)大學(xué)OJ答案、東方博宜OJ答案1065、1168這些關(guān)鍵詞。搜索結(jié)果倒是多但復(fù)制粘貼過去多半AC不了。這里面有幾層原因。第一不同學(xué)校的OJ測評系統(tǒng)版本不一樣對代碼的要求也有差異。有些老版本的OJ不支持C11的語法你復(fù)制一段用了auto、vector迭代器的代碼過去編譯階段直接報錯。第二不同題庫的數(shù)據(jù)范圍不同。同一個題號可能在不同OJ里數(shù)據(jù)范圍相差很大。上一家OJ允許O(n2)的算法換了數(shù)據(jù)范圍之后你的O(n2)就是不超時所以復(fù)制過來的解法在另一家OJ上可能會TLE或者WA。第三最扎心的一點很多人搜到的“答案”本身就是錯的。搜索引擎里排在前面的很多是學(xué)生上傳的作業(yè)代碼沒有經(jīng)過嚴(yán)格驗證。我見過復(fù)制答案提交上去得0分的也見過復(fù)制到一個能在本地運行的但OJ上判WA的。網(wǎng)上代碼的質(zhì)量魚龍混雜你不能指望搜出來的都是AC代碼。5.2 題解的正確打開方式不搜答案當(dāng)然是不可能的關(guān)鍵是怎么“搜”才有用。我建議的順序是第一遍完全自己做。哪怕卡住也要先卡到想不出來卡兩個小時比上來就搜答案有用得多。第二遍帶著自己的“未完成代碼”去搜思路搜到的不是源碼而是題解blog或者視頻講解重點看你沒想通的那一步是怎么處理的。第三遍如果真的有代碼思路對著思路自己動手寫千萬不要整段復(fù)制。復(fù)制代碼你收獲的是“粘貼通過”的假象自己寫一遍你收獲的是寫這段代碼的思路和手感。比如東方博宜1065和1168這類題搜出來的代碼可能有好幾個版本你需要對比著看哪種符合你自己的語言和思路。對比的過程也是學(xué)習(xí)你會看到同一個問題可以用循環(huán)、可以用遞歸、可以用棧看到多了你就有自己的判斷了。5.3 從“交作業(yè)”到“刷題”O(jiān)J的真正價值很多同學(xué)覺得OJ就是老師布置作業(yè)的“工具”交完就和自己沒關(guān)系。這個認(rèn)知對學(xué)編程來說基本是自廢武功。OJ的核心價值在于它以極低的成本給你提供反饋。你寫一個程序以前讓老師跑一遍得等上課現(xiàn)在你往OJ一交幾秒內(nèi)告訴你對錯、耗時、內(nèi)存開銷。這種即時反饋是自學(xué)編程最稀缺的資源。每一次WA、TLE都是系統(tǒng)在提醒你某個環(huán)節(jié)出了問題而找出這個問題的過程就是能力提升的過程。刷題帶來的能力不是刷題本身而是三種通用能力讀題能力——面對一段沒有自由發(fā)揮空間的問題描述你能精確抽取出輸入輸出和約束條件邊界思維——你不只會考慮正常情況還會考慮極端情況調(diào)試能力——面對沒有任何提示的“程序出錯”你能有系統(tǒng)的方法去縮小問題范圍。這三種能力在工作中寫業(yè)務(wù)代碼、排查線上bug、和同事協(xié)作全都用得上。6. 給初學(xué)者的實操建議與節(jié)奏安排6.1 第一道OJ題的完整流程示例假設(shè)你遇到的是“3.3 OJ”里最簡單的那種讀兩個整數(shù)輸出它們的和。看起來太簡單了但完整流程值得好好走一遍。第一步新建一個源文件C就按.cppJava就按.javaPython就按.py。第二步寫程序框架。C的話先寫出頭文件、命名空間、main函數(shù)。用scanf還是cin看個人習(xí)慣入門期建議用cin/cout直觀好用。第三步寫核心邏輯。用一個整型變量接收第一個數(shù)一個接收第二個數(shù)然后輸出它們的和。第四步本地編譯運行輸入樣例確認(rèn)輸出和樣例一致。第五步測試幾個邊界情況輸入0和0輸入負(fù)數(shù)和正數(shù)輸入很大的數(shù)。確認(rèn)輸出都合理。第六步檢查代碼里有沒有多余的調(diào)試輸出、提示語、注釋的freopen。全部清理掉。第七步提交到OJ。在頁面上選擇編程語言粘貼代碼提交等待結(jié)果。第一次見到AC的綠色狀態(tài)條彈出來你會很爽。這個爽感就是后續(xù)刷題的正反饋燃料。6.2 交不上、編譯錯、分?jǐn)?shù)低怎么辦編譯錯誤CECompile Error是最容易解決的問題因為OJ通常會告訴你編譯器給出的錯誤信息。把報錯信息復(fù)制到搜索引擎里搜基本都能找到答案。最常見的編譯錯類型包括頭文件沒包含用了sqrt沒加cmath、變量名拼寫不一致、結(jié)尾少了分號、大小寫寫錯了。如果是WA但是你在本地跑了樣例完全沒問題重新把題目讀一遍。很多時候我重新讀題才發(fā)現(xiàn)自己忽略了“多組數(shù)據(jù)”“輸入包含空格”“輸出結(jié)果后需要換行”這些關(guān)鍵信息。還有一種做法是自己把代碼里的核心邏輯口述一遍——給自己講一遍你到底在算什么往往講著講著就發(fā)現(xiàn)邏輯鏈條在哪斷了。如果你真的在某個題上卡了非常久我的建議是切換策略先做掉兩道簡單的水題找回手感再回來啃這道題。不是每一道題都必須當(dāng)場解決大腦會在后臺處理信息隔一晚上再看這道題思路往往豁然開朗。6.3 建立一個“刷題錯題本”第一道題AC之后千萬別急著沖下一道?;ㄎ宸昼妼憘€刷題筆記記下三件事題目讓你做什么你當(dāng)時的錯誤想法是什么正確的解法是什么。這個筆記不用很長幾個要點就行。為什么要做這個事因為OJ題的坑是高度類似的。數(shù)組越界、輸入輸出格式、數(shù)據(jù)溢出、邊界判斷這些坑會在幾百道題里反復(fù)出現(xiàn)。你踩過一次記下來下次就會條件反射地去檢查。不記的話同一個坑三個月后再踩一次的幾率非常高。等到期末或者面試前復(fù)習(xí)翻這個錯題本比把幾百道題重新刷一遍效率高太多了。6.4 編程環(huán)境與OJ評測環(huán)境的版本差異這里還有最后一個容易踩的坑你本地的編譯環(huán)境版本和OJ評測機的版本不一定一致。比如西北農(nóng)林科技大學(xué)這類高校的OJC評測環(huán)境可能是C11也可能是C17如果你本地用了C17的auto、結(jié)構(gòu)化綁定等新特性評測機編譯不過就是CE。解決思路是提交前看一眼題目頁面或者OJ幫助文檔里的編譯參數(shù)。大多數(shù)高校OJ都會寫清楚支持的編譯標(biāo)準(zhǔn)。另外一個保守做法是除非確實需要否則盡量使用C11之前的語法這能保證代碼在任何環(huán)境下都能編譯。智能指針、lambda表達式這些C高級特性在面試和工程里很有用但在OJ做題階段不是必需品別為了一時騷氣付出CE的代價。我個人做了幾年題、帶過幾屆學(xué)生之后最大的體會是OJ是一面很誠實的鏡子它會毫不留情地照出你對每一類問題的理解程度。騙得了自己騙不了評測機這一點比任何課程實驗報告都真實。這不只是“做作業(yè)”這是你一步步能實證自己邏輯能力的過程。3.3這道題可能就是你被這面鏡子照得最清楚的一次機會。