關於算法競賽某些常見基礎錯誤

手(shou)誤(jian)

  • 出錯特徵:程序執行流程出乎意料,結果不正確。c++

  • 出錯樣例:算法

    for (int i = 0; i < n; i++) {
        if (i = n)
            printf("%d\n", i);
        else
            printf("%d ", i);
    }
  • 治療方法:剁手。多剁兩次就記住了。編程

浮點數判等

  • 出錯特徵:WA到死。數組

  • 出錯樣例:函數

    double a = 1 / 3 * 3;
    double b = 1;
    if (a == b) {
        printf("Yes");
    }
  • 治療方法:測試

    const double eps = 1e-5;
    double a = 1 / 3 * 3;
    double b = 1;
    if (abs(a - b) < eps) {
        printf("Yes");
    }
  • 注意點:eps到底取多少? 通常在1e-5到1e-8之間。有些題目卡eps。(就是莫名其妙的一個wa一個ac)設計

聲明變量和使用變量太遠……

  • 出錯特徵:Output Limit Error 或 WA 或 RE 或 TE 或 機器爆炸。指針

  • 出錯樣例:code

    題目:計算a+b。ci

    輸入:t組數據,每組測試數據包含兩個數a,b。

    輸出:對於沒組數據,輸出a+b的值。每兩組輸出之間換行隔開

  • #include <cstdio>
    bool isFirst, t;
    int a, b;
    int main() {
        isFirst = true;
        scanf("%d", &t);
        while (t--) {
            scanf("%d%d", &a, &b);
            if (isFirst) {
                isFirst = false;
            } else {
                puts("");
            }
            printf("%d\n", a + b);
        }
        return 0;
    }
    數據:
    3
    1 2
    2 3
    3 4
  • 治療方法:先睡一覺。寫出這種代碼,你必定是太累了。

忘記初始化

  • 出錯特徵:WA
  • 出錯樣例:好比每次使用vis以前沒有清false之類。
  • 治療方案:
    • 每一個變量定義的同時就初始化。
    • 提交代碼以前,檢查全部定義的變量是否已經初始化。

數組開小了

  • 出錯特徵:差異不大的會WA或TE。差異大的會RE。
  • 出錯樣例:眼花手抖致使的數組少個0。,「樹」類問題數組只開了n(應該要4n)
  • 治療方案:數組開的足夠大。

Ctrl+C && Ctrl+V

  • 出錯說明:複製一段代碼而後粘貼再修改的方式編程。經常出現沒修改乾淨的問題。常出如今搜索題或輸出圖形的題。
  • 出錯特徵:WA
  • 出錯樣例:暫缺
  • 治療方法:
    • 不要複製代碼。把能重用的地方封裝成函數而後再用(每每比較費時間)
    • 採用複製代碼方式。修改後而後檢查3遍,當WA的時候,重點檢查此處。優先重寫此處。(即將複製的代碼列爲高危代碼)
  • PS:寫工程的時候,不要複製代碼……除非你的工程不須要維護(好比做業)

建議的代碼書寫方式

  • 良好的代碼風格。包括但不限於
    • 有意義的變量名(起名字真的是個技術,沒那麼簡單,就能找到,聊得來的伴
    • 縮進
    • 大括號的位置(選擇一個風格保持統一)
    • 有必要的空格使代碼清晰(好比:int a = (10*3/2 + 10)/3
    • C++式的變量聲明方式(即等到要用的時候再聲明,不要在函數開頭聲明一堆,而後再用)
  • 防護性編程
    • 聲明變量後當即初始化,無論是否必要。
    • 指針不用後當即清空,無論是否必要。

浮點數相關的陷阱

偏差修正

簡述

由於被計算機表示浮點數的方式所限制,CPU在進行浮點數計算時會出現偏差。如執行0.1 + 0.2 == 0.3結果每每爲false,在四則運算中,加減法對精度的影響較小,而乘法對精度的影響更大,除法的對精度的影響最大。因此,在設計算法時,爲了提升最終結果的精度,要儘可能減小計算的數量,尤爲是乘法和除法的數量。

浮點數與浮點數之間不能直接比較,要引入一個eps常量。eps是epsilon(εε)的簡寫,在數學中每每表明任意小的量。在對浮點數進行大小比較時,若是他們的差的絕對值小於這個量,那麼咱們就認爲他們是相等的,從而避免了浮點數精度偏差對浮點數比較的影響。eps在大部分題目時取1e-8就夠了,但要根據題目實際的內容進行調整。

模板代碼

// sgn返回x通過eps處理的符號,負數返回-1,正數返回1,x的絕對值若是足夠小,就返回0。
const double eps = 1e-8;
int sgn(double x) { return x < -eps ? -1 : x > eps ? 1 : 0; }
整型比較 等價的浮點數比較
a == b sgn(a - b) == 0
a > b sgn(a - b) > 0
a >= b sgn(a - b) >= 0
a < b sgn(a - b) < 0
a <= b sgn(a - b) <= 0
a != b sgn(a - b) != 0

輸入輸出

scanf輸入浮點數時,double的佔位符是%lf,可是浮點數doubleprintf系列函數中的標準佔位符是%f而不是%lf,使用時最好使用前者,由於雖而後者在大部分的計算機和編譯器中能獲得正確結果,但在有些狀況下會出錯(好比在POJ上)。

開方

當提供給C語言中的標準庫函數double sqrt (double x)x爲負值時,sqrt會返回nan,輸出時會顯示成nan-1.#IND00(根據系統的不一樣)。在進行計算幾何編程時,常常有對接近零的數進行開方的狀況,若是輸入的數是一個極小的負數,那麼sqrt會返回nan這個錯誤的結果,致使輸出錯誤。解決的方法就是將sqrt包裝一下,在每次開方前進行判斷。

示例代碼

double mysqrt(double x) { return max(0.0, sqrt(x)); }

負零

大部分的標程的輸出是不會輸出負零的,以下面這段程序:

int main() {
    printf("%.2f\n", -0.0000000001);
    return 0;
}

會輸出-0.00。有時這樣的結果是錯誤的,因此在沒有Special Judge的題目要求四捨五入時,不要忘記對負零進行特殊判斷。

但有的標程也不會進行這樣的特殊判斷,因此在WA時不要放棄摸索。

寫在最後

「年代久遠」的緣故,上面的錯誤已經找不到原來的代碼了。 若是各位刷題時出現鬧鬼代碼,歡迎編輯在此,以供後人抓鬼。

相關文章
相關標籤/搜索