Mayoritas Wrong Answer, Runtime Error, dan Time Limit Exceeded di kompetisi pemrograman disebabkan oleh lima jenis kesalahan yang sama, berulang. Kesalahan-kesalahan ini bukan masalah algoritma — solusi sudah benar secara konsep — tetapi masalah implementasi kecil yang lolos verifikasi sample tetapi gagal di hidden test. Pada Chapter ini kita akan membedah lima kesalahan paling umum: integer overflow, off-by-one, uninitialized variable, array out-of-bounds, dan TLE dari algoritma suboptimal. Tujuan akhirnya: setelah membaca chapter ini, kita dapat mengenali pola kesalahan pada pandangan pertama dan menulis kode yang bebas bug sejak awal.

Lima Kesalahan Paling Umum #

#MistakeVerdict TipikalCara Hindari
1Integer overflow pada akumulatorWA atau UBPakai long long untuk sum
2Off-by-one pada loop (<= vs <)WAPakai i < n, bukan i <= n
3Uninitialized variableWA atau UBSelalu inisialisasi eksplisit
4Array out-of-boundsRE (segfault)Validasi indeks sebelum akses
5TLE dari algoritma O(n²) pada n = 10^5TLEPakai O(n log n) atau lebih baik

Mekanisme: Mengapa Lima Ini Begitu Sering Muncul #

  1. Integer overflow: tipe int 32-bit hanya menampung ±2.1 × 10^9. Untuk soal dengan n = 10^5 elemen masing-masing 10^9, sum dapat mencapai 10^14 — jauh melebihi batas int. Signed overflow adalah UB; compiler boleh mengasumsikan tidak terjadi.

  2. Off-by-one: batas loop i <= n mengakses a[n] yang tidak ada. Untungnya pada std::vector::operator[] ini UB tetapi tidak crash; ASan melaporkan, tanpa ASan hasil tak terduga.

  3. Uninitialized variable: int x; tanpa = berisi nilai tak terduga dari memori stack. Compiler modern memberi warning -Wuninitialized; selalu aktifkan.

  4. Array OOB: akses a[n] pada array int a[n]. Indeks valid adalah [0, n-1]. Ini menyebabkan segfault atau UB.

  5. TLE: algoritma O(n²) pada n = 10^5 = 10^10 operasi ≈ 100 detik. Time limit 1 detik → TLE pasti. Harus O(n log n) atau O(n).

Kode: Lima Mistake dan Versi Aman #

cpp
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>

// === Mistake 1: Integer overflow pada akumulator ===
int sum_int_bug(const std::vector<int>& a) {
    int s = 0;
    for (int x : a) s += x;  // overflow bila |sum| > 2^31
    return s;
}
long long sum_ll_ok(const std::vector<int>& a) {
    long long s = 0;
    for (int x : a) s += x;
    return s;
}

// === Mistake 2: Off-by-one pada loop ===
int count_loop_bug(const std::vector<int>& a, int threshold) {
    int cnt = 0;
    // BUG: i <= size() -> akses a[n] yang OOB
    for (size_t i = 0; i <= a.size(); ++i) {
        if (i < a.size() && a[i] < threshold) cnt++;
    }
    return cnt;
}
int count_loop_ok(const std::vector<int>& a, int threshold) {
    int cnt = 0;
    for (size_t i = 0; i < a.size(); ++i) {
        if (a[i] < threshold) cnt++;
    }
    return cnt;
}

// === Mistake 3: Uninitialized variable ===
int uninit_bug() {
    int x;            // UB bila dibaca
    return x + 1;     // bisa apa saja
}
int uninit_ok() {
    int x = 0;
    return x + 1;
}

// === Mistake 4: Array OOB causing RE ===
int array_oob_ok() {
    int a[5] = {1, 2, 3, 4, 5};
    int sum = 0;
    for (int i = 0; i < 5; ++i) sum += a[i];  // i < 5 (aman)
    return sum;
}

// === Mistake 5: TLE dari O(n^2) ===
void tle_bug(std::vector<int>& a) {
    // Bubble sort O(n^2) — TLE bila n=10^5 (100 detik)
    int n = (int)a.size();
    for (int i = 0; i < n - 1; ++i)
        for (int j = 0; j < n - i - 1; ++j)
            if (a[j] > a[j+1]) std::swap(a[j], a[j+1]);
}
void tle_ok(std::vector<int>& a) {
    std::sort(a.begin(), a.end());  // O(n log n)
}

int main() {
    std::cout << "=== Lima Common Mistakes di CP ===\n\n";

    std::cout << "1) Integer overflow pada akumulator\n";
    std::vector<int> big(100000, 100000);  // sum = 10^10
    std::cout << "   sum_int_bug(big) = " << sum_int_bug(big) << " (SALAH, overflow)\n";
    std::cout << "   sum_ll_ok(big)  = " << sum_ll_ok(big) << " (BENAR)\n\n";

    std::cout << "2) Off-by-one pada loop\n";
    std::vector<int> v = {5, 3, 8, 1, 9};
    std::cout << "   count_loop_bug(v, 5) = " << count_loop_bug(v, 5) << "\n";
    std::cout << "   count_loop_ok(v, 5)  = " << count_loop_ok(v, 5) << " (BENAR)\n\n";

    std::cout << "3) Uninitialized variable\n";
    std::cout << "   uninit_bug() = " << uninit_bug() << " (UB — nilai tak terduga)\n";
    std::cout << "   uninit_ok()  = " << uninit_ok() << " (selalu 1 — aman)\n\n";

    std::cout << "4) Array out-of-bounds (RE)\n";
    std::cout << "   array_oob_ok() = " << array_oob_ok() << " (BENAR)\n\n";

    std::cout << "5) TLE dari O(n^2) pada n=10^5\n";
    std::vector<int> small = {5, 2, 8, 1, 9, 3, 7, 4, 6};
    std::vector<int> copy1 = small, copy2 = small;
    tle_bug(copy1);
    tle_ok(copy2);
    std::cout << "   tle_bug (bubble O(n^2)): hasil = ";
    for (int x : copy1) std::cout << x << ' ';
    std::cout << '\n';
    std::cout << "   tle_ok  (std::sort O(n log n)): hasil = ";
    for (int x : copy2) std::cout << x << ' ';
    std::cout << "\n   Catatan: untuk n=10^5, bubble sort TLE ~100 detik.\n\n";

    std::cout << "=== Tabel Ringkasan Mistake ===\n";
    std::cout << "Mistake                | Verdict Tipikal | Cara Hindari\n";
    std::cout << "-----------------------|-----------------|-----------------------------\n";
    std::cout << "Integer overflow       | WA / UB         | Pakai long long untuk sum\n";
    std::cout << "Off-by-one             | WA              | Pakai i < n, bukan i <= n\n";
    std::cout << "Uninitialized variable | WA / UB         | Selalu inisialisasi eksplisit\n";
    std::cout << "Array OOB              | RE (segfault)   | Validasi indeks sebelum akses\n";
    std::cout << "TLE dari O(n^2)        | TLE             | Pakai O(n log n) atau lebih baik\n";
    std::cout << "MLE dari struktur besar| MLE             | Hitung memori sebelum deklarasi\n";
    std::cout << "Wrong output format    | WA / PE         | Hati-hati newline & separator\n";
    std::cout << "Stack overflow rekursi | RE              | Pakai iteratif untuk n besar\n";
    return 0;
}

Output yang Dihasilkan:

text
=== Lima Common Mistakes di CP ===

1) Integer overflow pada akumulator
   sum_int_bug(big) = 1410065408 (SALAH, overflow)
   sum_ll_ok(big)  = 10000000000 (BENAR)

2) Off-by-one pada loop
   count_loop_bug(v, 5) = 2
   count_loop_ok(v, 5)  = 2 (BENAR)

3) Uninitialized variable
   uninit_bug() = 1 (UB — nilai tak terduga)
   uninit_ok()  = 1 (selalu 1 — aman)

4) Array out-of-bounds (RE)
   array_oob_ok() = 15 (BENAR)

5) TLE dari O(n^2) pada n=10^5
   tle_bug (bubble O(n^2)): hasil = 1 2 3 4 5 6 7 8 9 
   tle_ok  (std::sort O(n log n)): hasil = 1 2 3 4 5 6 7 8 9 
   Catatan: untuk n=10^5, bubble sort TLE ~100 detik.

=== Tabel Ringkasan Mistake ===
Mistake                | Verdict Tipikal | Cara Hindari
-----------------------|-----------------|-----------------------------
Integer overflow       | WA / UB         | Pakai long long untuk sum
Off-by-one             | WA              | Pakai i < n, bukan i <= n
Uninitialized variable | WA / UB         | Selalu inisialisasi eksplisit
Array OOB              | RE (segfault)   | Validasi indeks sebelum akses
TLE dari O(n^2)        | TLE             | Pakai O(n log n) atau lebih baik
MLE dari struktur besar| MLE             | Hitung memori sebelum deklarasi
Wrong output format    | WA / PE         | Hati-hati newline & separator
Stack overflow rekursi | RE              | Pakai iteratif untuk n besar

Bedah Kode Baris-per-Baris #

  1. int s = 0; for (int x : a) s += x; (bug): tipe int 32-bit. Untuk 100000 × 100000 = 10^10, overflow menjadi 1410065408 (sisa modular 2^32). Versi long long menghitung dengan benar 10000000000.
  2. for (size_t i = 0; i <= a.size(); ++i) (bug): loop berjalan n+1 kali — mengakses a[n] yang OOB. Pada demo, kita tambahkan if (i < a.size()) sebagai jaring pengaman, tetapi ini menyembunyikan bug, bukan memperbaiki. Versi i < a.size() langsung benar.
  3. int x; return x + 1; (bug): variabel x tidak diinisialisasi. Compiler modern memberi warning -Wuninitialized. Pada demo, gcc kebetulan mengembalikan 1 (mungkin memori zero-initialized), tetapi ini UB — bisa apa saja.
  4. for (int i = 0; i < 5; ++i) sum += a[i]; (aman): batas i < 5, indeks [0, 4] valid. Bandingkan dengan i <= 5 yang mengakses a[5] (OOB).
  5. Bubble sort O(n²) vs std::sort O(n log n): untuk n = 9, keduanya selesai dalam mikrodetik. Untuk n = 10^5, bubble = 10^10 operasi ≈ 100 detik (TLE), std::sort = 1.7 × 10^6 operasi ≈ 17 ms (selesai).

Pola Verbose Compiler yang Sering Diabaikan #

WarningPenyebabTindakan
variable may be used uninitializedVariabel tidak diinisialisasi sebelum dibacaInisialisasi eksplisit
comparison between signed and unsignedsize_t vs intCast atau gunakan size_t konsisten
unused variableVariabel dideklarasi tetapi tidak dipakaiHapus atau gunakan
control reaches end of non-void functionFungsi tidak return di semua cabangTambah return default
narrowing conversionInisialisasi int dengan long longCast eksplisit atau perbaiki tipe

Verdict Cheatsheet #

Bila mendapat verdict selain AC, langkah diagnosis pertama:

VerdictPenyebab UmumTindakan
WAOff-by-one, overflow, salah tipe dataCek edge case, ganti long long
TLEAlgoritma suboptimal, infinite loopHitung kompleksitas, cek loop
MLEStruktur data terlalu besarHitung memori, kurangi ukuran
REArray OOB, null deref, stack overflowASan, cek batas rekursi
CESintaks salah, header hilangBaca pesan compiler

Checklist Anti-Mistake Sebelum Submit #

ItemVerifikasi
1. Akumulator sum/product pakai long long bila perluHitung n × max_val
2. Loop condition pakai < bukan <=Kecuali spesifik soal
3. Semua variabel lokal diinisialisasiint x = 0; bukan int x;
4. Akses array valid [0, n-1]Validasi indeks sebelum akses
5. Kompleksitas n × ops ≤ 10^8Sesuai time limit
6. Memori ≤ 256 MBHitung struktur data terbesar
7. Output dengan '\n' di akhirBukan endl, jangan lupa newline
8. Compile dengan -Wall -Wextra tanpa warningAktifkan sejak awal

Ringkasan #

  • Lima mistake paling umum: integer overflow, off-by-one, uninitialized variable, array OOB, TLE dari O(n²).
  • long long wajib untuk akumulator sum bila n × max_val mendekati atau melebihi INT_MAX.
  • Loop condition < bukan <= — kecuali soal spesifik meminta inklusif.
  • Selalu inisialisasi variabel lokal secara eksplisit (int x = 0;).
  • std::sort (O(n log n)) mengalahkan bubble sort (O(n²)) untuk n ≥ 1000.
  • Compile dengan -Wall -Wextra -Wuninitialized untuk menangkap bug sejak awal.
  • Verdict bukan diagnosa akurat — gunakan tabel diagnosis untuk arah investigasi.