04. Percabangan — Pembahasan PR

Berkas .cpp jawabannya ada di exercises/04/homework/solutions/.

Halaman ini dibuka sesudah mencoba sendiri. Bandingkan jalan pikiranmu dengan bagian Cara berpikir, bukan kodenya baris per baris: dua kode yang berbeda bentuk bisa sama-sama benar, dan yang perlu ditiru adalah cara sampai ke sana. Bagian Uji di tiap soal berisi masukan dan keluaran yang sudah diperiksa — jalankan jawabanmu sendiri dengan masukan itu sebelum merasa selesai.

Menjalankan salah satu jawabannya:

g++ -std=c++17 -O2 -o jawab exercises/04/homework/solutions/01-ganjil-genap.cpp
./jawab

Soal 1 — Ganjil atau genap

Soal. Baca satu bilangan bulat, cetak genap atau ganjil, termasuk untuk bilangan negatif.

Cara berpikir. n % 2 bernilai 0 untuk genap. Untuk bilangan negatif, C++ memberi -1 (bukan 1) pada bilangan ganjil. Karena itu memeriksa n % 2 == 0 aman, sedangkan memeriksa n % 2 == 1 salah untuk -3.

Pembahasan.

int n;
cin >> n;

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

Uji.

Masukan Keluaran
4 genap
7 ganjil
0 genap
-4 genap

Kasus -4 itulah inti soalnya. Memeriksa n % 2 == 0 aman untuk bilangan negatif, tetapi memeriksa n % 2 == 1 untuk "ganjil" tidak: -7 % 2 bernilai -1 di C++, bukan 1, sehingga -7 akan salah digolongkan genap. Aman selalu: periksa == 0 dan taruh kasus ganjil di else.

Kesalahan umum.

  • Menulis if (n % 2 == 1) lalu gagal pada masukan negatif.
  • Menulis if (n = 2). Satu = mengisi nilai, dan kondisinya jadi selalu benar.

Soal 2 — Terbesar dari tiga

Soal. Cetak nilai terbesar dari tiga bilangan, tanpa max.

Cara berpikir. Jangan mencoba menuliskan semua kemungkinan urutan — ada enam, dan kasus seri membuatnya makin berantakan. Pakai pola juara bertahan: anggap bilangan pertama yang terbesar, lalu tantang satu per satu. Pola ini nanti dipakai lagi untuk mencari nilai terbesar dalam array.

Kasus seri selesai dengan sendirinya karena pembandingnya > (bukan >=): saat dua angka sama besar, yang pertama bertahan dan hasil cetaknya tetap benar.

Pembahasan.

int besar = a;
if (b > besar) besar = b;
if (c > besar) besar = c;

cout << besar << "\n";

Uji.

Masukan Keluaran
3 9 5 9
7 7 7 7
-5 -2 -9 -2

Dua baris terakhir adalah kasus tepi yang diminta soal: nilai yang sama besar tidak boleh membuat program gagal memilih, dan data yang seluruhnya negatif tidak boleh dijawab 0.

Kesalahan umum.

  • Menulis if (a > b && a > c) ... else if (b > a && b > c) ... lalu lupa satu cabang, sehingga untuk masukan 5 5 3 tidak ada yang tercetak.

Soal 3 — Tahun kabisat

Soal. Cetak KABISAT atau BUKAN.

Cara berpikir. Aturan "kecuali, kecuali lagi" lebih mudah ditulis sebagai satu ungkapan logika:

  • habis dibagi 400 → pasti kabisat, atau
  • habis dibagi 4 dan tidak habis dibagi 100.

Pembahasan.

bool kabisat = (tahun % 400 == 0) || (tahun % 4 == 0 && tahun % 100 != 0);

if (kabisat) cout << "KABISAT\n";
else cout << "BUKAN\n";

Bentuk bertingkat di bawah ini juga benar, dan kadang lebih mudah dibaca:

if (tahun % 4 != 0) cout << "BUKAN\n";
else if (tahun % 100 != 0) cout << "KABISAT\n";
else if (tahun % 400 != 0) cout << "BUKAN\n";
else cout << "KABISAT\n";

Uji.

Masukan Keluaran Alasannya
2024 KABISAT habis dibagi 4, bukan kelipatan 100
2023 BUKAN tidak habis dibagi 4
1900 BUKAN kelipatan 100, bukan kelipatan 400
2000 KABISAT kelipatan 400

Keempat baris itu mewakili keempat cabang aturannya. Kalau salah satu saja tidak diuji, kesalahan urutan pemeriksaan bisa lolos tanpa terlihat.

Kesalahan umum.

  • Hanya memeriksa tahun % 4 == 0, sehingga 1900 dijawab kabisat.
  • Menaruh pemeriksaan % 100 sebelum % 400 pada bentuk ||, sehingga tahun 2000 dijawab bukan kabisat.

Soal 4 — Jenis segitiga

Soal. Tentukan apakah tiga sisi membentuk segitiga, lalu jenisnya.

Cara berpikir. Syarat segitiga: jumlah dua sisi terpendek harus lebih besar dari sisi terpanjang. Supaya tidak perlu memeriksa tiga pasangan, urutkan dulu ketiga nilainya sehingga a <= b <= c; setelah itu satu pemeriksaan a + b <= c sudah cukup.

Mengurutkan tiga nilai dengan tangan butuh tiga penukaran: a–b, b–c, lalu a–b sekali lagi.

Urutan pemeriksaan jenisnya juga penting: periksa sama sisi dulu, baru sama kaki. Kalau dibalik, segitiga sama sisi ikut terjaring sebagai sama kaki.

Pembahasan.

if (a > b) { int t = a; a = b; b = t; }
if (b > c) { int t = b; b = c; c = t; }
if (a > b) { int t = a; a = b; b = t; }

if (a + b <= c) {
    cout << "BUKAN SEGITIGA\n";
} else if (a == b && b == c) {
    cout << "SAMA SISI\n";
} else if (a == b || b == c) {
    cout << "SAMA KAKI\n";
} else {
    cout << "SEMBARANG\n";
}

Uji.

Masukan Keluaran
3 3 3 SAMA SISI
5 5 8 SAMA KAKI
3 4 5 SEMBARANG
1 2 10 BUKAN SEGITIGA

Urutan pemeriksaannya penting: syarat "bukan segitiga" harus diperiksa lebih dulu. Kalau tidak, 1 2 10 akan dijawab SEMBARANG padahal ketiga sisi itu tidak bisa membentuk segitiga sama sekali.

Kesalahan umum.

  • Memakai a + b < c sebagai syarat gagal. Segitiga dengan sisi 1 2 3 tidak ada (ketiga titiknya segaris), jadi tandanya harus <=.
  • Lupa mengurutkan, lalu memeriksa hanya satu pasangan sisi.
  • Memeriksa sama kaki sebelum sama sisi.

Tantangan — Harga tiket

Soal. Beberapa aturan diskon bisa berlaku bersamaan; ambil yang termurah.

Cara berpikir. Menyusun if / else if di sini menyulitkan, karena else if akan berhenti di aturan pertama yang cocok — padahal soal meminta yang termurah di antara semua yang cocok. Pola yang tepat: simpan harga terbaik sejauh ini, lalu periksa setiap aturan secara terpisah dan perbarui bila lebih murah. Ini pola juara bertahan lagi, kali ini mencari yang terkecil.

Aturan umur < 5 dipisah di akhir karena nilainya pasti nol dan mengalahkan semua.

Pembahasan.

const int DASAR = 50000;
int harga = DASAR;

int setengah = DASAR / 2;          // 25000
int diskon20 = DASAR * 80 / 100;   // 40000

if (umur >= 5 && umur <= 17 && setengah < harga) harga = setengah;
if (pelajar == 1 && umur >= 18 && diskon20 < harga) harga = diskon20;
if (umur >= 60 && setengah < harga) harga = setengah;
if (umur < 5) harga = 0;

cout << harga << "\n";

Periksa dengan masukan 65 1: pelajar berumur 65. Aturan pelajar memberi 40000, aturan lansia memberi 25000; jawabannya 25000.

Uji.

Masukan Keluaran Aturan yang menang
4 0 0 di bawah 5 tahun: gratis
12 1 25000 5–17 tahun: setengah
20 1 40000 pelajar dewasa: diskon 20%
65 0 25000 60 tahun ke atas: setengah
30 0 50000 tidak ada diskon

Perhatikan baris 12 1: umur 12 dan berstatus pelajar mengenai dua aturan sekaligus. Yang dipakai yang paling murah, yaitu setengah harga, bukan diskon 20 persen.

Kesalahan umum.

  • Memakai else if sehingga pelajar lansia tetap membayar 40000.
  • Menghitung diskon dengan DASAR * 0.8 lalu mencetaknya sebagai double, sehingga keluarannya 40000.0 dan ditolak penilai. Kalikan dulu baru bagi (DASAR * 80 / 100) agar tetap bilangan bulat.

Latihan bergaya OSN — pembahasan

Berkas jawabannya ada di exercises/04/osn/solutions/.

Soal 1 — Tarif Parkir

Cara berpikir. Ada empat aturan, dan tiga di antaranya saling mengesampingkan, jadi bentuknya rantai if / else if / else. Urutannya mengikuti urutan aturan dari yang paling khusus: durasi nol lebih dulu, lalu dua jam pertama, lalu selebihnya.

Bagian yang perlu dipikirkan adalah "atau bagian dari jam". Kalimat itu berarti pembulatan ke atas, dan pembulatan ke atas untuk bilangan bulat ditulis (sisa + 59) / 60: menambah 59 membuat sisa berapa pun, sekecil apa pun, terdorong melewati kelipatan 60 berikutnya.

Batas atas 50000 bukan cabang tersendiri melainkan pemotongan di akhir, karena ia berlaku untuk hasil berapa pun.

Pembahasan.

int durasi = keluar - masuk;
int tarif;

if (durasi == 0) {
    tarif = 0;
} else if (durasi <= 120) {
    tarif = 5000;
} else {
    int sisa = durasi - 120;
    int jam = (sisa + 59) / 60;       // pembulatan ke atas
    tarif = 5000 + 3000 * jam;
    if (tarif > 50000) tarif = 50000;
}

cout << tarif << "\n";

Uji.

Masukan Keluaran Yang diperiksa
480 480 0 durasi nol
480 600 5000 tepat 120 menit, belum ada tambahan
480 601 8000 satu menit lewat sudah dihitung sejam
600 780 8000 tepat tiga jam
0 1440 50000 tarif tertinggi

Baris kedua dan ketiga adalah pasangan yang wajib diuji bersama: keduanya hanya berbeda satu menit, dan di situlah letak kesalahan yang paling sering.

Kesalahan umum.

  • Memakai sisa / 60, yang membulatkan ke bawah, sehingga 121 menit dihargai 5000.
  • Menuliskan batas 50000 sebagai cabang else if, sehingga tidak berlaku pada perhitungan cabang lain.
  • Lupa memisahkan durasi nol, sehingga parkir nol menit dikenakan 5000.

Soal 2 — Jam Digital

Cara berpikir. Satu hari berisi 86400 detik. Karena jam digital tidak menunjukkan tanggal, kelebihan harinya dibuang dengan % 86400. Sesudah itu, pemecahannya adalah pasangan / dan % yang sama seperti memecah digit di Bab 03:

  • jam: hari / 3600
  • menit: (hari % 3600) / 60
  • detik: hari % 60

Bagian HH:MM:SS menuntut dua digit, jadi angka di bawah 10 perlu didahului 0. Karena setw dan setfill belum dibahas, nol itu dicetak sendiri dengan if.

Pembahasan.

long long hari = t % 86400;
long long jam = hari / 3600;
long long menit = (hari % 3600) / 60;
long long detik = hari % 60;

if (jam < 10) cout << "0";
cout << jam << ":";
if (menit < 10) cout << "0";
cout << menit << ":";
if (detik < 10) cout << "0";
cout << detik << "\n";

Dengan <iomanip>, ketiga pasang baris itu bisa diringkas menjadi cout << setw(2) << setfill('0') << jam dan seterusnya. Keduanya benar.

Uji.

Masukan Keluaran Yang diperiksa
0 00:00:00 semua bagian nol
3661 01:01:01 contoh soal
86399 23:59:59 detik terakhir dalam sehari
86400 00:00:00 tepat satu hari penuh
1000000000 01:46:40 T jauh di atas satu hari

Kesalahan umum.

  • Lupa % 86400, sehingga T besar mencetak jam yang lebih dari 23.
  • Menghitung menit sebagai hari / 60, bukan (hari % 3600) / 60.
  • Mencetak 1:1:1. Format HH:MM:SS menuntut dua digit di setiap bagian.

Soal 3 — Juri Lomba

Cara berpikir. Setiap juri diperiksa dengan pola yang sama, sehingga ketiganya ditulis tiga kali dalam bentuk identik. Perhatikan bahwa ada tiga kemungkinan per juri, bukan dua: A menang, B menang, atau tidak ada yang dipilih. Karena itu bentuknya if / else if tanpa else — kasus ketiga memang tidak melakukan apa-apa.

Sesudah suara dihitung, perbandingan terakhirnya juga bertiga kemungkinan.

Pembahasan.

int suaraA = 0, suaraB = 0;

if (a1 > b1) suaraA++;
else if (b1 > a1) suaraB++;

if (a2 > b2) suaraA++;
else if (b2 > a2) suaraB++;

if (a3 > b3) suaraA++;
else if (b3 > a3) suaraB++;

if (suaraA > suaraB) cout << "A\n";
else if (suaraB > suaraA) cout << "B\n";
else cout << "SERI\n";

Uji.

Masukan Keluaran Yang diperiksa
9 8 7 9 10 6 A contoh soal
1 2 3 3 4 5 B ada juri yang tidak memilih
5 5 5 5 5 5 SERI tidak ada juri yang memilih

Kasus ketiga penting: hasilnya seri bukan karena suaranya sama banyak, melainkan karena tidak ada suara sama sekali. Jawaban yang hanya membandingkan "siapa yang lebih banyak" tanpa cabang else terakhir akan mencetak baris kosong di kasus ini.

Kesalahan umum.

  • Memakai else pada pemeriksaan per juri, sehingga nilai sama dihitung sebagai kemenangan B.
  • Menjumlahkan nilai ketiga juri lalu membandingkan totalnya. Itu soal yang berbeda: peserta bisa menang total nilai tetapi kalah suara juri, seperti pada 1 10 5 4 5 4.
  • Mencetak Seri atau seri. Soal meminta SERI.

Kalau masih tersendat

  • Rantai if memilih cabang yang salah → Bab 04, tabel penelusuran pada bagian if, else if, else.
  • Menerjemahkan kalimat soal menjadi syarat → Bab 04, bagian Menerjemahkan kalimat soal menjadi kondisi.
  • Menggabungkan beberapa syarat → Bab 04, bagian Menggabungkan kondisi dan tabel kebenarannya.

Sebelum lanjut ke Bab 05, pastikan kamu bisa menulis syarat untuk "umurnya antara 5 dan 17 tahun" beserta kebalikannya, tanpa ragu pada tanda < dan <=.