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 #
| # | Mistake | Verdict Tipikal | Cara Hindari |
|---|---|---|---|
| 1 | Integer overflow pada akumulator | WA atau UB | Pakai long long untuk sum |
| 2 | Off-by-one pada loop (<= vs <) | WA | Pakai i < n, bukan i <= n |
| 3 | Uninitialized variable | WA atau UB | Selalu inisialisasi eksplisit |
| 4 | Array out-of-bounds | RE (segfault) | Validasi indeks sebelum akses |
| 5 | TLE dari algoritma O(n²) pada n = 10^5 | TLE | Pakai O(n log n) atau lebih baik |
Mekanisme: Mengapa Lima Ini Begitu Sering Muncul #
Integer overflow: tipe
int32-bit hanya menampung±2.1 × 10^9. Untuk soal dengann = 10^5elemen masing-masing10^9, sum dapat mencapai10^14— jauh melebihi batasint. Signed overflow adalah UB; compiler boleh mengasumsikan tidak terjadi.Off-by-one: batas loop
i <= nmengaksesa[n]yang tidak ada. Untungnya padastd::vector::operator[]ini UB tetapi tidak crash; ASan melaporkan, tanpa ASan hasil tak terduga.Uninitialized variable:
int x;tanpa=berisi nilai tak terduga dari memori stack. Compiler modern memberi warning-Wuninitialized; selalu aktifkan.Array OOB: akses
a[n]pada arrayint a[n]. Indeks valid adalah[0, n-1]. Ini menyebabkan segfault atau UB.TLE: algoritma
O(n²)padan = 10^5=10^10operasi ≈ 100 detik. Time limit 1 detik → TLE pasti. HarusO(n log n)atauO(n).
Kode: Lima Mistake dan Versi Aman #
#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:
=== 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 besarBedah Kode Baris-per-Baris #
int s = 0; for (int x : a) s += x;(bug): tipeint32-bit. Untuk100000 × 100000 = 10^10, overflow menjadi1410065408(sisa modular 2^32). Versilong longmenghitung dengan benar10000000000.for (size_t i = 0; i <= a.size(); ++i)(bug): loop berjalann+1kali — mengaksesa[n]yang OOB. Pada demo, kita tambahkanif (i < a.size())sebagai jaring pengaman, tetapi ini menyembunyikan bug, bukan memperbaiki. Versii < a.size()langsung benar.int x; return x + 1;(bug): variabelxtidak diinisialisasi. Compiler modern memberi warning-Wuninitialized. Pada demo, gcc kebetulan mengembalikan 1 (mungkin memori zero-initialized), tetapi ini UB — bisa apa saja.for (int i = 0; i < 5; ++i) sum += a[i];(aman): batasi < 5, indeks[0, 4]valid. Bandingkan dengani <= 5yang mengaksesa[5](OOB).- Bubble sort
O(n²)vsstd::sort O(n log n): untukn = 9, keduanya selesai dalam mikrodetik. Untukn = 10^5, bubble =10^10operasi ≈ 100 detik (TLE),std::sort=1.7 × 10^6operasi ≈ 17 ms (selesai).
Pola Verbose Compiler yang Sering Diabaikan #
| Warning | Penyebab | Tindakan |
|---|---|---|
variable may be used uninitialized | Variabel tidak diinisialisasi sebelum dibaca | Inisialisasi eksplisit |
comparison between signed and unsigned | size_t vs int | Cast atau gunakan size_t konsisten |
unused variable | Variabel dideklarasi tetapi tidak dipakai | Hapus atau gunakan |
control reaches end of non-void function | Fungsi tidak return di semua cabang | Tambah return default |
narrowing conversion | Inisialisasi int dengan long long | Cast eksplisit atau perbaiki tipe |
Verdict Cheatsheet #
Bila mendapat verdict selain AC, langkah diagnosis pertama:
| Verdict | Penyebab Umum | Tindakan |
|---|---|---|
| WA | Off-by-one, overflow, salah tipe data | Cek edge case, ganti long long |
| TLE | Algoritma suboptimal, infinite loop | Hitung kompleksitas, cek loop |
| MLE | Struktur data terlalu besar | Hitung memori, kurangi ukuran |
| RE | Array OOB, null deref, stack overflow | ASan, cek batas rekursi |
| CE | Sintaks salah, header hilang | Baca pesan compiler |
Checklist Anti-Mistake Sebelum Submit #
| Item | Verifikasi |
|---|---|
1. Akumulator sum/product pakai long long bila perlu | Hitung n × max_val |
2. Loop condition pakai < bukan <= | Kecuali spesifik soal |
| 3. Semua variabel lokal diinisialisasi | int x = 0; bukan int x; |
4. Akses array valid [0, n-1] | Validasi indeks sebelum akses |
5. Kompleksitas n × ops ≤ 10^8 | Sesuai time limit |
| 6. Memori ≤ 256 MB | Hitung struktur data terbesar |
7. Output dengan '\n' di akhir | Bukan endl, jangan lupa newline |
8. Compile dengan -Wall -Wextra tanpa warning | Aktifkan sejak awal |
Ringkasan #
- Lima mistake paling umum: integer overflow, off-by-one, uninitialized variable, array OOB, TLE dari
O(n²). long longwajib untuk akumulator sum bilan × max_valmendekati atau melebihiINT_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²)) untukn ≥ 1000.- Compile dengan
-Wall -Wextra -Wuninitializeduntuk menangkap bug sejak awal. - Verdict bukan diagnosa akurat — gunakan tabel diagnosis untuk arah investigasi.