04. Percabangan

Sampai sini program berjalan sama persis setiap kali dijalankan. Bab ini menambahkan kondisi: pertanyaan yang dijawab benar atau salah, dan jawabannya menentukan bagian kode mana yang dijalankan.

Operator perbandingan

Operator Arti
== sama dengan
!= tidak sama dengan
< kurang dari
> lebih dari
<= kurang dari atau sama dengan
>= lebih dari atau sama dengan

Hasilnya bertipe bool: true (dicetak 1) atau false (dicetak 0).

cout << (7 == 7) << "\n";    // 1
cout << (7 != 8) << "\n";    // 1
cout << (3 >= 5) << "\n";    // 0

Di C++, false adalah 0 dan true adalah 1. Sebaliknya, setiap nilai yang bukan nol dianggap benar. Itulah sebabnya if (x) sah ditulis dan berarti "kalau x tidak nol", dan if (n % 2) berarti "kalau n ganjil".

== membandingkan, = memberi nilai. if (x = 7) bukan error sintaks di C++ — ia mengisi x dengan 7 lalu menganggap kondisinya benar. Program jadi salah tanpa satu pun pesan compiler. Periksa ulang setiap = di dalam if.

if, else if, else

int nilai = 73;

if (nilai >= 90) {
    cout << "A\n";
} else if (nilai >= 80) {
    cout << "B\n";
} else if (nilai >= 70) {
    cout << "C\n";
} else if (nilai >= 60) {
    cout << "D\n";
} else {
    cout << "E\n";
}

Cara membacanya: bayangkan nilai 73 menuruni tangga dan berhenti di anak tangga pertama yang bisa dilewati.

nilai >= 90 >= 80 >= 70 >= 60 tercetak
95 ya — — — A
90 ya — — — A
89 tidak ya — — B
73 tidak tidak ya — C
59 tidak tidak tidak tidak E

Tanda — berarti kondisi itu tidak pernah diperiksa: begitu satu cabang terpilih, sisanya dilewati seluruhnya.

Perhatikan juga bahwa cabang else if (nilai >= 80) tidak perlu ditulis nilai >= 80 && nilai < 90. Kalau baris itu sampai diperiksa, berarti nilai >= 90 sudah terbukti salah. Menambahkan syarat kedua tidak salah, hanya membuat kode lebih panjang dan lebih mudah salah ketik.

C++ memeriksa kondisi dari atas ke bawah dan berhenti di yang pertama benar. Urutan karena itu menentukan hasil: kalau nilai >= 60 diletakkan paling atas, semua nilai lulus akan mendapat D.

Kurung kurawal boleh dihilangkan kalau isinya satu pernyataan:

if (n % 2 == 0) cout << "genap\n";
else cout << "ganjil\n";

Di lomba, bentuk satu baris itu sering dipakai karena lebih cepat ditulis. Tetapi begitu isinya bertambah jadi dua pernyataan, kurung kurawal wajib ditambahkan. Lupa menambahkannya menghasilkan bug yang sulit terlihat:

if (a > b)
    cout << a << "\n";
    cout << "terbesar\n";   // selalu dicetak, tidak peduli kondisinya

Buka exercises/04/01-nilai-huruf.cpp. Ubah nilai menjadi tepat 90, 89, 70, dan 59, compile ulang tiap kali, dan pastikan batas >= berperilaku seperti dugaanmu.

Menggabungkan kondisi

Operator Arti
&& dan — benar jika kedua sisi benar
|| atau — benar jika salah satu sisi benar
! bukan — membalik benar dan salah

Tabel kebenarannya:

a b a && b a || b !a
salah salah salah salah benar
salah benar salah benar benar
benar salah salah benar salah
benar benar benar benar salah

Ringkasnya: && menuntut keduanya, || cukup salah satu.

int umur = 14;
bool punya_izin = true;

if (umur >= 12 && punya_izin) cout << "boleh ikut\n";
if (umur < 12 || !punya_izin) cout << "belum boleh ikut\n";

Dalam matematika kita menulis 1 <= x <= 10. Di C++ bentuk itu tidak berarti demikian: 1 <= x dihitung dulu menjadi 0 atau 1, lalu dibandingkan dengan 10 — hasilnya selalu benar. Tulis x >= 1 && x <= 10.

Hubung singkat

&& berhenti menghitung begitu sisi kirinya salah, dan || berhenti begitu sisi kirinya benar. Sifat ini dipakai untuk mengamankan pemeriksaan:

if (i < n && a[i] == target) ...

Kalau i < n salah, a[i] tidak pernah disentuh — ini yang mencegah program membaca di luar array. Menukar urutannya membuat program bisa berhenti mendadak.

Membalik kondisi

Kadang lebih mudah menuliskan kebalikan sebuah syarat. Aturan pembaliknya (hukum De Morgan) hanya dua baris, dan layak dihafal:

Kondisi Kebalikannya
a && b !a || !b
a || b !a && !b
x >= 1 && x <= 10 x < 1 || x > 10

Perhatikan bahwa && berubah menjadi || saat dibalik. Kesalahan yang sering terjadi: kebalikan dari "umurnya antara 5 dan 17" ditulis umur < 5 && umur > 17 — syarat yang mustahil benar, sehingga cabangnya tidak pernah dijalankan.

Menerjemahkan kalimat soal menjadi kondisi

Sebagian besar kesalahan percabangan di lomba terjadi saat menerjemahkan kalimat Indonesia menjadi tanda pembanding, bukan saat menulis kodenya:

Kalimat di soal Kondisinya
"lebih dari k" x > k
"paling banyak k", "tidak lebih dari k" x <= k
"paling sedikit k", "minimal k" x >= k
"antara a dan b" (termasuk ujungnya) x >= a && x <= b
"di luar rentang a–b" x < a || x > b
"kelipatan k", "habis dibagi k" x % k == 0
"a dan b berbeda" a != b
"salah satu dari a atau b genap" a % 2 == 0 || b % 2 == 0
"keduanya genap" a % 2 == 0 && b % 2 == 0

Kata "paling banyak" dan "paling sedikit" adalah sumber kesalahan satu langkah yang paling mahal, karena programnya tetap jalan dan hanya salah pada data di tepi batas. Kalau ragu, uji dengan nilai tepat di batasnya — untuk "paling banyak 10", periksa x = 10 dan x = 11.

Buka exercises/04/04-pola-soal.cpp. Di dalamnya ada beberapa kondisi yang sering muncul di soal. Tambahkan satu kondisi baru: mencetak BISA kalau sebuah bilangan habis dibagi 3 dan tidak habis dibagi 9.

switch

Kalau satu variabel bulat dibandingkan dengan banyak nilai tetap, switch lebih rapi:

int hari;
cin >> hari;

switch (hari) {
    case 1: cout << "Senin\n"; break;
    case 2: cout << "Selasa\n"; break;
    case 3: cout << "Rabu\n"; break;
    default: cout << "hari lain\n";
}

break wajib ada di setiap case; tanpanya, program lanjut mengerjakan case berikutnya (disebut fall-through). switch hanya bekerja untuk tipe bulat dan char, tidak untuk string atau rentang seperti nilai >= 90.

Di lomba, switch jarang dipakai karena if bisa mengerjakan semuanya. Ia tetap perlu dikenali, karena soal OSN tingkat kabupaten sering menampilkan potongan switch lalu bertanya apa keluarannya — dan jawabannya bergantung pada ada atau tidaknya break.

Buka exercises/04/03-switch.cpp. Hapus satu break, compile ulang, lalu jalankan dengan masukan yang mengenai case itu. Perhatikan dua baris ikut tercetak sekaligus.

Operator tiga bagian

Bentuk kondisi ? nilai_jika_benar : nilai_jika_salah memilih satu dari dua nilai dalam satu pernyataan:

int a = 7, b = 12;
int besar = (a > b) ? a : b;
cout << (besar % 2 == 0 ? "genap" : "ganjil") << "\n";

Berguna untuk kasus pendek. Untuk logika bercabang banyak, if tetap lebih mudah dibaca.

Buka exercises/04/02-logika.cpp, lalu ubah umur dan punya_izin sampai setiap baris keluaran pernah muncul.

Pola yang sering muncul di soal

// genap atau ganjil
if (n % 2 == 0) ...

// kelipatan
if (n % 5 == 0) ...

// di dalam rentang
if (x >= 1 && x <= 100) ...

// terbesar dari dua
int besar = (a > b) ? a : b;

// tanda bilangan
if (n > 0) cout << "positif\n";
else if (n < 0) cout << "negatif\n";
else cout << "nol\n";

Pekerjaan rumah

Soal 1 — Ganjil atau genap

Buka exercises/04/homework/01-ganjil-genap.cpp. Baca satu bilangan bulat, cetak genap atau ganjil. Uji juga dengan bilangan negatif — pastikan jawabanmu tetap benar untuk -4.

Soal 2 — Terbesar dari tiga

Buka exercises/04/homework/02-terbesar.cpp. Baca tiga bilangan bulat, cetak yang terbesar. Pakai if dan operator perbandingan saja, tanpa max, tanpa array. Pastikan jawabanmu tetap benar jika ada dua angka yang sama besar.

Soal 3 — Tahun kabisat

Buka exercises/04/homework/03-kabisat.cpp. Baca satu tahun, cetak KABISAT atau BUKAN. Aturannya: habis dibagi 4, kecuali habis dibagi 100, kecuali lagi habis dibagi 400. Jadi 2000 kabisat, 1900 bukan.

Soal 4 — Jenis segitiga

Buka exercises/04/homework/04-segitiga.cpp. Baca tiga bilangan bulat positif sebagai panjang sisi. Cetak BUKAN SEGITIGA bila jumlah dua sisi terpendek tidak melebihi sisi terpanjang. Kalau segitiga, cetak salah satu dari SAMA SISI, SAMA KAKI, atau SEMBARANG.

Tantangan — Harga tiket

Buka exercises/04/homework/05-tiket.cpp. Baca umur dan status pelajar (1 untuk pelajar, 0 bukan). Harga dasar 50000. Aturan diskonnya:

  • umur di bawah 5 tahun: gratis,
  • umur 5 sampai 17 tahun: bayar setengah,
  • pelajar berumur 18 tahun ke atas: diskon 20 persen,
  • umur 60 tahun ke atas: bayar setengah,
  • selain itu: harga penuh.

Kalau lebih dari satu aturan cocok, pakai yang paling murah. Cetak harganya sebagai bilangan bulat.

Kesalahan yang paling sering

Yang ditulis Akibatnya
if (x = 7) mengisi, bukan membandingkan; kondisinya selalu benar
if (1 <= x <= 10) selalu benar; tulis x >= 1 && x <= 10
if (a > b); titik koma menutup if; isinya selalu dijalankan
dua pernyataan tanpa { } hanya pernyataan pertama yang ikut kondisi
urutan else if terbalik cabang yang longgar menangkap semuanya lebih dulu
case tanpa break cabang berikutnya ikut dijalankan
> padahal soal berkata "paling banyak" salah satu langkah di tepi batas

Titik koma setelah if adalah kesalahan yang paling sulit dilihat mata, karena kodenya tetap di-compile tanpa peringatan apa pun:

if (n % 2 == 0);              // titik koma ini menutup if
    cout << "genap\n";        // selalu dicetak

Tips OSN

Soal percabangan adalah soal membaca. Di OSN tingkat awal, sebagian besar soal if tidak sulit secara teknik; yang menjatuhkan adalah satu kalimat aturan yang terlewat. Kebiasaan yang menolong: salin daftar aturannya ke dalam komentar di atas kode, satu baris satu aturan, lalu coret satu per satu setelah ditulis menjadi if.

Uji tepat di batas, bukan di tengah. Kalau aturan berbunyi "umur 17 tahun ke bawah", uji dengan 16, 17, dan 18 — bukan dengan 10 dan 40. Hampir semua kesalahan percabangan bersembunyi di satu angka di perbatasan, dan berkas uji OSN hampir selalu memuat angka itu.

Urutan cabang adalah bagian dari jawaban. Rantai if / else if berhenti di cabang pertama yang benar. Aturan yang lebih khusus harus diperiksa lebih dulu daripada aturan umum:

if (n % 100 == 0) ...        // khusus: kelipatan 100
else if (n % 4 == 0) ...     // umum: kelipatan 4

Kalau urutannya ditukar, cabang kedua tidak pernah tercapai untuk 100, 200, 300, dan seterusnya.

Kalau soal menyebut "pilih yang paling menguntungkan", jangan memakai rantai else if. Aturan yang saling tumpang tindih tidak menghasilkan satu cabang, melainkan beberapa calon jawaban. Hitung semua calonnya, lalu ambil yang terkecil atau terbesar sesuai permintaan soal.

Keluaran teks harus sama persis. Kalau soal meminta YA dan TIDAK, maka Ya, ya, atau YES semuanya dinilai salah. Salin kata-katanya dari teks soal, jangan diketik ulang dari ingatan.

Kasus tepi yang paling sering dilupakan:

Bentuk soal Kasus tepi yang wajib diuji
bilangan bisa negatif -1, 0, dan -n genap seperti -4
ada pembagian penyebut bernilai 0
ada rentang a sampai b a, b, a-1, b+1
membandingkan tiga nilai ketiganya sama, dua sama satu berbeda
ada selisih waktu atau jarak selisih nol

if (x = 5) bukan perbandingan, melainkan penugasan yang selalu bernilai benar. Compiler sering hanya memberi peringatan, bukan error, sehingga programnya tetap jadi dan tetap salah. Kalau sebuah cabang selalu dimasuki padahal seharusnya tidak, periksa jumlah tanda = lebih dulu.

Ringkasan

  • Hasil perbandingan bertipe bool; false adalah 0, dan setiap nilai bukan nol dianggap benar.
  • Rantai if / else if / else diperiksa dari atas dan berhenti di cabang pertama yang benar, jadi urutannya menentukan hasil.
  • && menuntut kedua sisi benar, || cukup salah satu, ! membalik. Saat dibalik, && menjadi || dan sebaliknya.
  • && dan || berhenti menghitung begitu hasilnya sudah pasti; sifat ini dipakai untuk mengamankan pemeriksaan seperti i < n && a[i] == x.
  • Tulis rentang sebagai x >= 1 && x <= 10, bukan 1 <= x <= 10.
  • Kesalahan percabangan lebih sering lahir dari salah menerjemahkan kalimat soal daripada dari salah menulis kode. Uji selalu dengan nilai tepat di batasnya.

Latihan bergaya OSN

Ketiga soal berikut adalah soal percabangan murni: tidak ada perulangan, tidak ada array, tetapi aturannya bertumpuk seperti pada soal sungguhan. Kerjakan daftar aturannya satu per satu, dan uji tepat di batasnya.

Kerangka ketiganya ada di exercises/04/osn/.

Soal 1 — Tarif Parkir

Sebuah gedung menghitung tarif parkir dengan aturan berikut:

  • dua jam pertama dikenakan tarif tetap 5000 rupiah;
  • setiap jam berikutnya, atau bagian dari jam, ditambah 3000 rupiah;
  • tarif satu hari tidak pernah melebihi 50000 rupiah;
  • kendaraan yang masuk dan keluar pada menit yang sama tidak dikenakan biaya.

Format masukan. Satu baris berisi dua bilangan bulat M dan K: menit kendaraan masuk dan menit kendaraan keluar, dihitung sejak pukul 00:00.

Format keluaran. Satu baris berisi tarif parkir dalam rupiah.

Batasan. 0 ≤ M ≤ K ≤ 1440.

Contoh masukan

480 601

Contoh keluaran

8000

Penjelasan. Kendaraan parkir selama 601 − 480 = 121 menit. Dua jam pertama (120 menit) dikenakan 5000. Sisanya 1 menit, dan satu menit pun dihitung sebagai satu jam penuh, sehingga ditambah 3000. Totalnya 8000.

Kerangkanya: exercises/04/osn/01-tarif-parkir.cpp

Soal 2 — Jam Digital

Sebuah jam digital menampilkan waktu dalam format HH:MM:SS, masing-masing tepat dua digit. Jam itu dinyalakan pada pukul 00:00:00 dan dibiarkan berjalan selama T detik. Tentukan angka yang tertera di layarnya pada akhir percobaan.

Format masukan. Satu baris berisi satu bilangan bulat T.

Format keluaran. Satu baris berisi waktu dalam format HH:MM:SS.

Batasan. 0 ≤ T ≤ 10⁹.

Contoh masukan

3661

Contoh keluaran

01:01:01

Penjelasan. 3661 detik adalah 1 jam, 1 menit, dan 1 detik. Perhatikan bahwa T boleh lebih dari satu hari: jam digital tidak menunjukkan tanggal, jadi kelebihan harinya diabaikan. Untuk T = 86400, layarnya kembali ke 00:00:00.

Kerangkanya: exercises/04/osn/02-jam-digital.cpp

Soal 3 — Juri Lomba

Dua peserta, A dan B, dinilai oleh tiga juri. Setiap juri memberi satu nilai untuk A dan satu nilai untuk B. Juri dianggap memilih peserta yang ia beri nilai lebih tinggi; kalau ia memberi nilai yang sama untuk keduanya, juri itu tidak memilih siapa pun.

Pemenangnya adalah peserta yang dipilih oleh lebih banyak juri. Kalau keduanya dipilih oleh jumlah juri yang sama, hasilnya seri.

Format masukan. Satu baris berisi enam bilangan bulat a1 b1 a2 b2 a3 b3: nilai A dan nilai B dari juri pertama, lalu dari juri kedua, lalu dari juri ketiga.

Format keluaran. Satu baris berisi A, B, atau SERI.

Batasan. Setiap nilai berada di antara 0 dan 100.

Contoh masukan

9 8 7 9 10 6

Contoh keluaran

A

Penjelasan. Juri pertama memilih A (9 > 8), juri kedua memilih B (9 > 7), juri ketiga memilih A (10 > 6). A mendapat dua suara, B satu suara.

Kerangkanya: exercises/04/osn/03-juri-lomba.cpp

Bingung atau sudah selesai? Buka halaman pembahasan PR.