03. Variabel, tipe data, dan masukan — Pembahasan PR
Berkas .cpp jawabannya ada di
exercises/03/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/03/homework/solutions/01-jumlah-dua.cpp
./jawab
Soal 1 — Jumlah dua bilangan
Soal. Baca dua bilangan bulat, cetak jumlahnya, tanpa teks tambahan.
Cara berpikir. Bentuk baku semua soal penilai
daring: deklarasikan variabel, baca dengan cin, cetak hasil
dengan cout. Yang membedakan jawaban benar dan salah di
sini bukan logikanya, melainkan disiplin format — satu kalimat pengantar
saja sudah membuat nilainya nol.
Pembahasan.
int a, b;
cin >> a >> b;
cout << a + b << "\n";Uji.
| Masukan | Keluaran |
|---|---|
3 4 |
7 |
-5 5 |
0 |
1000000000 1000000000 |
2000000000 |
Baris ketiga adalah kasus tepi yang layak diperhatikan: 2 × 10⁹ masih
muat di int, tetapi hanya nyaris (batasnya 2.147.483.647).
Kalau soalnya menyebut batas lebih tinggi sedikit saja, jawabannya harus
long long.
Kesalahan umum.
- Menambahkan
cout << "Masukkan dua bilangan: ";. - Mencetak
"Jumlahnya adalah "sebelum angkanya. - Memakai
intpadahal soal aslinya bisa saja menyebut batas sampai 10⁹ untuk masing-masing bilangan; jumlah dua bilangan sebesar itu masih muat diint, tetapi begitu batasnya naik, ganti kelong long.
Soal 2 — Rata-rata tiga nilai
Soal. Baca tiga bilangan bulat, cetak rata-rata dengan dua desimal.
Cara berpikir. Jebakannya pembagian bulat.
(80 + 85 + 91) / 3 memberi 85, bukan
85.33, karena semua bilangannya bulat. Bagi dengan
3.0 supaya C++ menghitung secara pecahan. Untuk mengatur
jumlah desimal, pakai fixed dan
setprecision(2) dari <iomanip>.
Pembahasan.
int a, b, c;
cin >> a >> b >> c;
double rata = (a + b + c) / 3.0;
cout << fixed << setprecision(2) << rata << "\n";Uji.
| Masukan | Keluaran |
|---|---|
7 8 9 |
8.00 |
1 1 2 |
1.33 |
-3 0 4 |
0.33 |
Baris kedua adalah pemeriksaan terpenting: kalau jawabanmu mencetak
1.00, berarti pembagiannya masih dikerjakan sebagai
bilangan bulat.
Kesalahan umum.
- Menulis
/ 3lalu heran hasilnya tidak ada koma. - Memakai
setprecision(2)tanpafixed. Tanpafixed, angka 2 berarti "dua digit berarti", sehingga85.33tercetak85.
Soal 3 — Ubah detik
Soal. 3725 detik menjadi
1 2 5.
Cara berpikir. Satu jam 3600 detik, satu menit 60
detik. Pakai / untuk mengambil banyaknya satuan besar dan
% untuk menyimpan sisanya:
- jam = total / 3600
- sisa setelah jam = total % 3600
- menit = sisa / 60
- detik = total % 60
Pembahasan.
int total;
cin >> total;
int jam = total / 3600;
int menit = (total % 3600) / 60;
int detik = total % 60;
cout << jam << " " << menit << " " << detik << "\n";Uji.
| Masukan | Keluaran |
|---|---|
3725 |
1 2 5 |
59 |
0 0 59 |
86399 |
23 59 59 |
Baris kedua menguji kasus kurang dari satu menit — jam dan menitnya
harus tetap dicetak sebagai 0, bukan dilewati. Baris ketiga
menguji nilai tepat satu detik sebelum satu hari penuh.
Kesalahan umum.
- Menulis
menit = total / 60tanpa membuang jamnya lebih dulu; untuk3725hasilnya62, bukan2. - Lupa bahwa
detik = total % 60sudah benar tanpa perantara, karena%60 tidak terpengaruh jam.
Soal 4 — Digit terakhir dan sisanya
Soal. 4071 menjadi 1 lalu
407.
Cara berpikir. Dua operasi yang akan kamu pakai
terus menerus di soal teori bilangan: n % 10 mengambil
digit terakhir, n / 10 membuangnya. Menggabungkan keduanya
di dalam perulangan adalah cara memproses semua digit sebuah bilangan
(Bab 05).
Pembahasan.
long long n;
cin >> n;
cout << n % 10 << "\n";
cout << n / 10 << "\n";Uji.
| Masukan | Keluaran (dua baris) |
|---|---|
4071 |
1 lalu 407 |
7 |
7 lalu 0 |
1000 |
0 lalu 100 |
Baris kedua adalah kasus tepi bilangan satu digit: membuang digit
terakhirnya menyisakan 0, bukan kosong. Baris ketiga
memastikan digit 0 tetap tercetak sebagai jawaban yang
sah.
Kesalahan umum.
- Mengubah bilangan menjadi
stringuntuk mengambil karakter terakhir. Bisa, tetapi lebih panjang dan lebih mudah salah daripada% 10. - Memakai
intuntuk bilangan yang soalnya mengizinkan sampai 10¹⁸.
Tantangan — Deteksi luapan
Soal. Perbaiki perkalian yang hasilnya salah.
Cara berpikir. 2000000 * 1500000
bernilai 3 × 10¹², jauh di atas batas int (sekitar 2,1 ×
10⁹). Hasilnya "berputar" dan mencetak angka yang tidak masuk akal tanpa
pesan error apa pun.
Yang sering terlewat: mengubah variabel penampung saja tidak cukup.
int a = 2000000, b = 1500000;
long long hasil = a * b; // masih salahPerkaliannya tetap dihitung sebagai int dulu, baru hasil
yang sudah rusak disalin ke long long. Perbaikan yang
benar: jadikan operandnya long long.
Pembahasan.
long long a = 2000000;
long long b = 1500000;
long long hasil = a * b;
cout << hasil << "\n"; // 3000000000000Kalau a dan b harus tetap int
(misalnya karena dibaca dari masukan sebagai int), paksa
satu sisinya:
long long hasil = (long long)a * b;Uji. Program ini tidak membaca masukan. Sesudah diperbaiki, keluarannya:
3000000000000
Sebelum diperbaiki, angkanya berbeda dan bisa berbeda pula antar compiler — itu sendiri petunjuk bahwa yang terjadi adalah luapan, bukan salah hitung.
Kesalahan umum.
- Mengira hasil yang salah berarti compiler rusak. Compiler tidak memeriksa luapan bilangan bulat; program tetap berjalan dengan angka yang salah.
- Memakai
doublesebagai jalan pintas.doublehanya menyimpan sekitar 15 digit berarti, sehingga untuk bilangan bulat besar hasilnya bisa meleset.
Latihan bergaya OSN — pembahasan
Berkas jawabannya ada di
exercises/03/osn/solutions/.
Soal 1 — Ubin Lantai
Cara berpikir. Ubin tidak boleh dipotong, jadi
banyaknya ubin ke arah panjang adalah P / S dengan
pembagian bilangan bulat — pecahannya memang harus dibuang, karena ubin
yang tidak utuh tidak dihitung. Hal yang sama berlaku untuk lebar. Luas
sisanya adalah luas ruangan dikurangi luas yang tertutup ubin.
Sebelum menulis kode, periksa besarnya hasil: P dan
L bisa 10⁹, jadi P × L bisa mencapai 10¹⁸. Itu
melewati int dan masih muat di long long.
Pembahasan.
long long p, l, s;
cin >> p >> l >> s;
long long banyak = (p / s) * (l / s);
long long sisa = p * l - banyak * s * s;
cout << banyak << " " << sisa << "\n";Mendeklarasikan ketiganya sebagai long long sejak awal
menghilangkan kebutuhan cast di mana pun.
Uji.
| Masukan | Keluaran |
|---|---|
10 7 3 |
6 16 |
5 5 5 |
1 0 |
1000000000 1000000000 7 |
20408163020408164 11999999964 |
Baris kedua adalah kasus tepi yang mudah terlewat: ruangan yang
ukurannya pas membuat sisanya nol. Baris ketiga adalah pemeriksaan tipe
— dengan int, keluarannya berubah menjadi angka
negatif.
Kesalahan umum.
- Menghitung
(p * l) / (s * s). Hasilnya bukan jumlah ubin utuh: cara ini menghitung seolah-olah potongan sisa di dua tepi bisa disambung menjadi ubin baru. - Memakai
intuntukpdanl, sehinggap * lmeluap.
Soal 2 — Nilai Akhir
Cara berpikir. Jawabannya pecahan, jadi
perhitungannya harus dilakukan dalam double. Menulis bobot
sebagai 0.3 dan 0.4 sudah cukup: satu operand
bertipe double membuat seluruh perkaliannya dikerjakan
dalam double. Format keluarannya dikunci dengan
fixed dan setprecision(2).
Pembahasan.
int tugas, uts, uas;
cin >> tugas >> uts >> uas;
double akhir = 0.3 * tugas + 0.3 * uts + 0.4 * uas;
cout << fixed << setprecision(2) << akhir << "\n";fixed berarti "tulis dalam bentuk desimal biasa, bukan
notasi ilmiah", dan setprecision(2) berarti "dua digit di
belakang koma". Keduanya dari <iomanip> dan harus
disertakan.
Uji.
| Masukan | Keluaran |
|---|---|
80 75 90 |
82.50 |
100 100 100 |
100.00 |
0 0 1 |
0.40 |
Kesalahan umum.
- Menulis
30 / 100 * tugas.30 / 100adalah pembagian dua bilangan bulat dan hasilnya0, sehingga seluruh nilai akhir menjadi nol. - Lupa
#include <iomanip>, sehingga compiler menolaksetprecision. - Memakai
setprecision(2)tanpafixed. Tanpafixed, angka 2 berarti dua digit berarti —82.5tercetak82, dan100tercetak1e+02.
Soal 3 — Deret Aritmetika
Cara berpikir. Dua rumus baku:
- suku ke-
n:a + (n - 1) × b - jumlah
nsuku:n × (2a + (n - 1) × b) / 2
Rumus kedua adalah rata-rata suku pertama dan suku terakhir dikalikan
banyaknya suku. Pembagian dengan 2 selalu pas, karena
n × (n − 1) pasti genap.
Periksa besarnya: (n − 1) × b bisa mencapai 10⁶ × 10⁶ =
10¹², dan n × (2a + (n − 1)b) bisa mencapai sekitar 10¹⁸.
long long cukup, int tidak.
Pembahasan.
long long a, b, n;
cin >> a >> b >> n;
long long suku = a + (n - 1) * b;
long long jumlah = n * (2 * a + (n - 1) * b) / 2;
cout << suku << " " << jumlah << "\n";Uji.
| Masukan | Keluaran |
|---|---|
3 5 4 |
18 42 |
10 -3 5 |
-2 20 |
1000000 1000000 1000000 |
1000000000000 500000500000000000 |
Baris kedua memastikan beda negatif ikut tertangani; baris ketiga adalah pemeriksaan tipe.
Kesalahan umum.
- Menulis
a + n * buntuk suku ke-n. Suku pertama sudaha, jadi penambahannyan − 1kali, bukannkali. - Membagi dengan 2 terlalu awal:
n / 2 * (2 * a + (n - 1) * b)salah untuknganjil, karenan / 2membuang pecahannya lebih dulu. - Memakai
doublesupaya "aman". Untuk jumlah sebesar 5 × 10¹⁷,doublekehilangan digit terakhir dan mencetak angka yang hampir benar — yang tetap dinilai salah.
Kalau masih tersendat
- Hasil pembagian selalu bulat → Bab 03, bagian Aritmetika.
- Angka besar berubah negatif → Bab 03, bagian Kenapa luapan menghasilkan angka negatif.
- Bingung
cinmembaca sampai mana → Bab 03, bagian Membaca masukan dengancin.
Sebelum lanjut ke Bab 04, pastikan kamu bisa menjawab tanpa
menjalankan program: berapa hasil 7 / 2,
7 % 2, dan (double)7 / 2, dan tipe apa yang
dipakai untuk menampung jumlah 100.000 bilangan bernilai 10⁹.