這篇文章是寫給計程新手的指南,雖然我自己也不是什麼強者,但還是分享一下我自己當初修計程時累積的技巧和經驗,希望可以對未來計程卡關的人有所幫助。
編譯方法
考試的時候是使用 VScode,沒有網路,所以你必須使用 cmd 來編譯。
- 單檔案編譯
把 file.c 編譯成一個叫 run 的檔案
gcc file.c -o run
執行 run
./run
其中 file.c 是你的檔名,run 是編譯完的檔名,這兩個可以換掉。 - 多檔案編譯
把 file1.c 和 file2.c 編譯成一個叫 run 的檔案
gcc file1.c file2.c -o run
執行 run
./run
其中 file1.c 和 file2.c 是你的檔名,run 是編譯完的檔名,這三個可以換掉。
這個寫在課本的第 19 章,正常人根本一開始不會去讀到那邊,害我一開始不會編譯只能硬丟,結果吃一堆 CE,還好後來都有過。
編輯器
比起 VScode,我個人私心推 CP Editor,可惜考試時不能用,我自己平常練題的時候都是用這個,它是為了競程設計的 editor,所以有很多方便解題的功能。
不過當然,這篇文章不會強制要求用 CP Editor,其他的 editor 也是適用的。
錯誤訊息解讀
這邊會介紹常見的錯誤訊息和它們可能的成因。
CE (compiile error)
這大概是新手時期最常見的一個錯誤訊息,你可以從 terminal 中看到是哪裡出了問題,如果看不懂或是不懂為什麼報錯的話可以上網搜尋看看。等到對語法熟了之後這種問題就不太會是一個困擾了。
RE (runtime error)
成因
有很多原因會造成這個問題,不過比較有可能的大概是這幾個:
scanf 的時候忘記加 &
scanf("%d",n);
當程式發生問題的時候(不管是 RE 還是其他的錯誤訊息),請第一個先檢查是不是忘記加 &。
這看起來很蠢,對吧?
但相信我,你不會希望你 debug 半天的結果是出在這種問題上面,我自己也被這東西搞過好幾次,所以請小心。存取超過陣列範圍
int array[5];
printf("%d",array[5]);請記得 c 是從 0 開始數。
另外,我通常會把數量設的比上限大一點來避免邊界的 RE 問題。開超大陣列(也有可能會是 TLE)
int array[10000000000][10000000000];
一般情況下是不至於開到一個這麼大的陣列,會遇到這個狀況的大概是 hash table 的題目(以計程的範圍而言),像這樣:
struct cube{
char val[100];
int posx,posy,posz;
};
struct cube tower[1000][1000][1000];你開了一個 struct 的陣列,然後就太大爆掉了,請改用指標來存這些資料,像這樣。
struct cube{
char val[100];
int posx,posy,posz;
};
struct cube *tower[1000][1000][1000];遞迴太深
void endless(int i){
printf("%d",i);
endless(i+1);
}在計程的範圍內,會出現遞迴太深的大概是不小心進入無限遞迴,在寫遞迴的時候,請確保有遞迴的三要件:終止、處理、和遞迴。
void not_endless(int i){
if(i>10) return; // 終止:什麼時候要結束
printf("%d",i); // 處理:要做什麼
not_endless(i+1); // 遞迴:怎麼進到下一個遞迴
}對 NULL 取值
int *ptr = NULL;
printf("%d",*ptr);或是
typedef struct Node{
int val;
struct Node *next;
}node;
node *ptr = NULL;
printf("%d",ptr->val);當然,應該是沒有人會指定完 NULL 之後就取值,但是有可能一個原本不是 NULL 的東西在程式執行的過程中變成 NULL 了(通常會在遞迴時發生),或是你忘記對這個變數賦值了。
TLE (time limit exceed)
不外乎就是兩種,無限迴圈跟無限遞迴(前者可能性比較大)。
檢查一下你的 while、for、遞迴的終止條件有沒有漏洞。
如果都沒問題的話,一個可能是你用了太麻煩的算法導致你的程式跑太久才會得到答案,這部分就要自己想怎麼優化你的程式了。
WA (wrong answer)
各種奇怪的原因都有可能會 WA,包括上面那些問題也有可能會 WA。
一種情況是你想錯了,你的解法本身就有問題。
另一種情況就是你的程式有 bug(廢話),這時候就是考驗 debug 實力的時候了。
怎麼debug
設斷點
int main(){
...someting code
printf("I'm OK here!");
exit(0);
...someting code
}int counter = 0;
void loop(){
...someting code
if(counter>5){
printf("I'm OK here!");
exit(0);
}
else counter++;
...someting code
}如果你在 terminal 上看到了 I’m OK here! 就表示上半段的程式沒有問題,如果沒看到,就表示問題在上半段的程式,用這種方法可以很快找出問題的發生點。
你也可以讓它 print 某些變數的值來確認你的程式的運行狀況是不是符合你的預期。故意誘發錯誤訊息
假設我現在因為不明原因一直有某個測資 WA,然後我猜測可能是發生了某個情況。int main(){
...someting code
if(REcondition) exit(-1); // RE
...someting code
}如果 condition 是 true,那麼就會變成 RE,這樣我就可以猜測我在這個情況下會不會 RE,當然你也可以製造 WA 或是 TLE 等等。
int main(){
...someting code
if(TLEcondition){ //TLE
int a=0;
while(true) a++;
}
if(WAcondition){ // WA
printf("This must not be answer");
exit(0);
}
...someting code
}但計程的考試有提交次數限制,要注意這點。