03. Variabel, tipe data, dan masukan

Program yang berguna bekerja dengan data yang berubah-ubah. Bab ini memperkenalkan variabel, tipe-tipe data yang dipakai di lomba, operasi hitungnya, dan cara membaca masukan dengan format yang diterima penilai daring.

Variabel dan tipe

Di C++ setiap variabel diberi tipe saat dibuat. Tipe menentukan nilai apa yang boleh disimpan dan berapa besar memorinya.

int umur = 15;
long long penduduk = 8000000000LL;
double berat = 47.5;
char nilai = 'A';
bool lulus = true;
string nama = "Budi";

Bayangkan variabel sebagai kotak berlabel di dalam memori. Labelnya adalah namanya, ukurannya ditentukan tipenya, dan isinya boleh diganti kapan saja — tetapi tipenya tidak.

Ada dua kejadian berbeda yang sering tertukar:

int umur = 15;   // deklarasi: kotak baru dibuat, lalu diisi
umur = 16;       // penugasan: kotak yang sudah ada, isinya diganti
int umur = 17;   // error: kotak dengan nama itu sudah ada

Kotak yang dibuat tanpa diisi berisi nilai sampah, bukan nol:

int x;
cout << x << "\n";   // angka tak terduga, dan bisa berbeda tiap dijalankan

Compiler tidak selalu memperingatkan. Biasakan mengisi setiap variabel saat dibuat, misalnya int jumlah = 0; — terutama variabel penampung yang akan ditambah di dalam perulangan nanti.

Aturan penamaan: boleh huruf, angka, dan garis bawah, tetapi tidak boleh diawali angka dan tidak boleh memakai kata kunci C++ seperti int atau return. Huruf besar-kecil dibedakan, jadi nilai dan Nilai adalah dua kotak berbeda.

Tipe yang perlu kamu kuasai untuk OSN:

Tipe Isi Rentang kira-kira
int bilangan bulat −2.147.483.648 … 2.147.483.647 (±2 × 10⁹)
long long bilangan bulat besar ±9,2 × 10¹⁸
double bilangan pecahan ±1,7 × 10³⁰⁸, 15–16 digit berarti
char satu karakter 'A', 'z', '7'
bool benar atau salah true atau false
string teks butuh #include <string>

Ini sumber nilai nol yang paling sering di OSN: hasil perhitungan melebihi rentang int, lalu nilainya "berputar" menjadi angka negatif tanpa pesan error apa pun. Aturan praktis: kalau hasil mungkin lebih dari 2 × 10⁹, pakai long long.

int a = 2000000000;
int b = a + a;            // meluap, hasilnya negatif
long long c = 2000000000LL + 2000000000LL;   // benar: 4000000000

Akhiran LL memberi tahu compiler bahwa angka itu sendiri long long, bukan int yang kemudian disimpan ke long long.

Kenapa luapan menghasilkan angka negatif

int menempati 4 byte, yaitu 32 bit, dan satu bit dipakai untuk tanda. Yang muat hanyalah bilangan dari −2.147.483.648 sampai 2.147.483.647. Ketika hasil hitungan melewati ujung atas, nilainya tidak berhenti di sana melainkan berputar ke ujung bawah, persis seperti jarum jam yang melewati angka 12 lalu kembali ke 1:

int x = 2147483647;      // nilai int terbesar
cout << x + 1 << "\n";   // -2147483648

Dua contoh yang paling sering merusak jawaban di lomba:

Perhitungan Jawaban benar Hasil bila disimpan di int
2000000000 + 2000000000 4.000.000.000 −294.967.296
50000 * 50000 2.500.000.000 −1.794.967.296
1 + 2 + … + 1.000.000 500.000.500.000 1.784.293.664

Perhatikan baris kedua: 50000 sendiri kecil sekali, tetapi hasil kalinya tidak. Yang menentukan adalah besarnya hasil, bukan besarnya masukan.

Ada satu jebakan tambahan. Tipe hasil perkalian ditentukan oleh tipe kedua operandnya, bukan oleh tipe kotak tujuannya:

int a = 50000, b = 50000;
long long salah = a * b;                  // -1794967296: meluap sebelum disalin
long long benar = (long long)a * b;       // 2500000000

Perkalian a * b dihitung lebih dulu sebagai int, meluap di situ, baru hasilnya yang sudah rusak disalin ke long long. Memberi cast pada salah satu sisi membuat seluruh perkalian dikerjakan sebagai long long.

Kebiasaan yang murah dan menyelamatkan banyak nilai: kalau ragu, pakai long long. Program yang memakai long long untuk semua bilangan bulat hanya sedikit lebih lambat dan sedikit lebih boros memori, sedangkan program yang salah tipe menghasilkan nol.

Aritmetika

Operator Arti Contoh Hasil
+ tambah 7 + 2 9
- kurang 7 - 2 5
* kali 7 * 2 14
/ bagi 7 / 2 3
% sisa bagi (modulo) 7 % 2 1

Perhatikan 7 / 2 menghasilkan 3, bukan 3.5. Kalau kedua sisi bilangan bulat, C++ memakai pembagian bulat: hasil pecahannya dibuang. Untuk mendapat 3.5, minimal satu sisi harus pecahan:

cout << 7 / 2 << endl;        // 3
cout << 7 / 2.0 << endl;      // 3.5
cout << (double)7 / 2 << endl;// 3.5

Bentuk (double)7 disebut cast: memaksa nilai dibaca sebagai tipe lain.

Pembagian bulat memotong ke arah nol, jadi 7 / 2 menjadi 3 dan -7 / 2 menjadi -3 (bukan -4).

double menyimpan bilangan pecahan sebagai hampiran biner, jadi nilainya tidak selalu tepat:

cout << fixed << setprecision(17) << 0.1 + 0.2 << "\n";
0.30000000000000004

Akibatnya, jangan pernah membandingkan dua double dengan ==: 0.1 + 0.2 == 0.3 bernilai salah. Kalau perbandingan pecahan benar-benar diperlukan, periksa selisihnya: if (fabs(a - b) < 1e-9) (fabs ada di <cmath>). Lebih baik lagi, hindari double bila soalnya bisa dikerjakan dengan bilangan bulat — dan sebagian besar soal OSN bisa.

Operator % hanya untuk bilangan bulat, dan sangat sering dipakai:

cout << 17 % 5 << endl;   // 2
cout << 10 % 2 << endl;   // 0  -> 10 genap
cout << 123 % 10 << endl; // 3  -> digit terakhir
cout << 123 / 10 << endl; // 12 -> buang digit terakhir

Dua baris terakhir adalah trik yang dipakai berulang kali di soal OSN untuk memproses angka digit demi digit.

Untuk bilangan negatif, C++ membuat hasil % mengikuti tanda bilangan yang dibagi: -7 % 3 bernilai -1, bukan 2. Bab 14 membahas akibatnya dan cara memperbaikinya.

Urutan pengerjaan

Seperti di matematika, *, /, dan % dikerjakan lebih dulu daripada + dan -. Yang setingkat dikerjakan dari kiri ke kanan.

cout << 2 + 3 * 4 << "\n";      // 14, bukan 20
cout << (2 + 3) * 4 << "\n";    // 20
cout << 100 / 10 / 2 << "\n";   // 5  (dibaca (100/10)/2)

Di lomba, aturan praktisnya sederhana: kalau ragu, beri kurung. Kurung tambahan tidak memperlambat program sedikit pun, sedangkan salah urutan mengubah jawaban.

Penugasan singkat

int x = 10;
x += 5;    // sama dengan x = x + 5   -> 15
x -= 3;    // 12
x *= 2;    // 24
x /= 5;    // 4   (pembagian bulat)
x %= 3;    // 1
x++;       // 2   (tambah satu)
x--;       // 1   (kurang satu)

Membaca masukan dengan cin

cin >> x membaca satu nilai dari masukan standar dan menyimpannya di x. Spasi, tab, dan pindah baris sama-sama dianggap pemisah.

int a, b;
cin >> a >> b;
cout << a + b << endl;

Bayangkan masukan sebagai satu antrean karakter yang panjang. Setiap cin >> x melakukan tiga hal berurutan: melewati semua spasi, tab, dan pindah baris yang ada di depan; membaca karakter berikutnya selama masih masuk akal untuk tipe x; lalu berhenti tepat sebelum pemisah berikutnya dan meninggalkannya di antrean.

Karena spasi dan pindah baris diperlakukan sama, program itu menerima 3 4 maupun:

3
4

Keduanya sama bagi cin.

Urutan pembacaan harus sama persis dengan urutan yang dijanjikan soal. Kalau soal berkata "baris pertama berisi n, baris kedua berisi n bilangan", maka n dibaca lebih dulu, baru sisanya — dan jumlah cin >> yang dijalankan harus tepat sebanyak angka yang tersedia. Kelebihan satu cin >> membuat program menunggu masukan yang tidak akan pernah datang.

cin >> x dengan x bertipe int akan gagal kalau yang tersedia di antrean bukan angka. Saat gagal, x tidak terisi dan seluruh pembacaan berikutnya ikut gagal — biasanya terlihat sebagai program yang mencetak angka sampah atau tidak mencetak apa pun. Kalau keluaranmu aneh sejak awal, periksa dulu apakah urutan pembacaannya cocok dengan format masukan soal.

Format masukan dan keluaran untuk penilai

Di OSN, program dinilai mesin. Karena itu ada aturan ketat:

  • Jangan mencetak kalimat pengantar seperti Masukkan dua bilangan: . Penilai membandingkan keluaranmu karakter demi karakter; kalimat tambahan membuat jawaban dianggap salah.
  • Cetak persis seperti yang diminta soal, tanpa teks tambahan.
  • Spasi atau pindah baris di akhir biasanya dimaafkan, tetapi jangan mengandalkannya.

Jadi untuk soal "baca dua bilangan, cetak jumlahnya", jawaban yang benar adalah persis:

#include <iostream>
using namespace std;

int main() {
    int a, b;
    cin >> a >> b;
    cout << a + b << "\n";
    return 0;
}

Saat menjalankannya di komputer sendiri, ketik angkanya lalu tekan Enter; layar akan terlihat kosong sampai kamu memasukkan semua angka yang diminta. Itu normal.

Buka exercises/03/03-masukan.cpp, compile, lalu jalankan dan ketik 10 3 diikuti Enter. Perhatikan hasil / dan % untuk kedua angka itu.

Membaca teks satu baris

cin >> nama berhenti di spasi pertama, jadi Budi Santoso hanya terbaca Budi. Untuk membaca satu baris penuh, pakai getline:

string baris;
getline(cin, baris);

Kalau getline dipakai sesudah cin >> n, baris pertama yang terbaca sering kosong: cin >> meninggalkan karakter pindah baris di masukan. Perbaikannya: panggil cin.ignore(); sebelum getline.

Membaca sampai masukan habis

Sebagian soal tidak memberi tahu ada berapa angka. Pola bakunya:

int x;
while (cin >> x) {
    // proses x
}

cin >> x bernilai salah ketika masukan sudah habis, jadi perulangannya berhenti sendiri.

Mencetak bilangan pecahan

Secara bawaan cout mencetak double dengan enam digit berarti. Soal sering meminta jumlah angka di belakang koma tertentu:

#include <iomanip>

double rata = 7.0 / 3.0;
cout << fixed << setprecision(2) << rata << endl;   // 2.33

fixed berarti "hitung digit setelah koma", setprecision(2) menetapkan dua digit. Keduanya cukup ditulis sekali; pengaruhnya berlaku untuk semua pencetakan berikutnya.

Buka exercises/03/04-pecahan.cpp. Ubah setprecision(2) menjadi setprecision(6), compile ulang, dan bandingkan keluarannya.

Pekerjaan rumah

Soal 1 — Jumlah dua bilangan

Buka exercises/03/homework/01-jumlah-dua.cpp. Baca dua bilangan bulat dari masukan standar, cetak jumlahnya. Tanpa kalimat pengantar apa pun. Ini bentuk soal termudah yang ada di penilai daring — pastikan kamu bisa mengirimkannya dengan benar.

Soal 2 — Rata-rata tiga nilai

Buka exercises/03/homework/02-rata-rata.cpp. Baca tiga bilangan bulat, cetak rata-ratanya dengan dua angka di belakang koma. Hati-hati pembagian bulat.

Soal 3 — Ubah detik

Buka exercises/03/homework/03-detik.cpp. Baca satu bilangan bulat berisi jumlah detik, cetak dalam bentuk jam menit detik dipisahkan spasi. Contoh: masukan 3725 menghasilkan 1 2 5. Pakai / dan %.

Soal 4 — Digit terakhir dan sisanya

Buka exercises/03/homework/04-digit.cpp. Baca satu bilangan bulat positif, cetak dua baris: digit terakhirnya, lalu bilangan itu tanpa digit terakhir. Contoh: masukan 4071 menghasilkan 1 lalu 407.

Tantangan — Deteksi luapan

Buka exercises/03/homework/05-luapan.cpp. File berisi perkalian dua bilangan besar yang hasilnya salah karena disimpan di int. Jalankan dulu, catat hasil salahnya, lalu perbaiki dengan long long sehingga keluarannya benar. Tuliskan hasil sebelum dan sesudah perbaikan sebagai komentar di bagian atas file.

Kesalahan yang paling sering

Yang ditulis Akibatnya
int untuk hasil yang melebihi 2 × 10⁹ jawaban negatif tanpa pesan error apa pun
long long x = a * b; dengan a, b bertipe int meluap sebelum disalin; beri cast
int rata = jumlah / n; pecahannya terbuang; pakai (double)jumlah / n
if (a == b) pada dua double hampir selalu salah; bandingkan selisihnya
cout << "Masukkan n: "; keluaran tambahan membuat penilai menyatakan salah
getline langsung sesudah cin >> n terbaca baris kosong; panggil cin.ignore() dulu
variabel dipakai sebelum diisi nilai sampah, hasil berbeda tiap dijalankan

Tips OSN

Baca batasan sebelum menulis kode. Bagian Batasan pada soal OSN bukan hiasan: dari situlah tipe data ditentukan. Urutannya selalu sama — lihat nilai terbesar yang mungkin muncul, perkirakan hasil terbesarnya, baru pilih tipe. Yang menentukan adalah besarnya hasil, bukan besarnya masukan: dua bilangan int yang dikalikan sudah bisa melewati batas int.

Batasan pada soal Tipe yang aman untuk hasilnya
jawaban ≤ 2 × 10⁹ int
jawaban ≤ 9 × 10¹⁸ long long
jawaban berupa pecahan double dengan setprecision

Luapan tidak memberi pesan error. Program yang meluap tetap berjalan, tetap mencetak angka, dan tetap dinilai Wrong Answer. Kalau jawabanmu tiba-tiba negatif pada uji berbatasan besar padahal logikanya benar, tersangka pertama selalu int.

Hati-hati pada perkalian setengah jalan. Menyimpan hasil ke long long tidak menolong kalau perkaliannya sendiri dilakukan sebagai int:

int p = 1000000, l = 1000000;
long long luas = p * l;          // salah: p * l dihitung sebagai int
long long benar = (long long)p * l;  // benar

Cara paling aman di lomba: kalau ada kemungkinan sedikit saja hasilnya besar, deklarasikan variabelnya long long sejak awal. Biaya kecepatannya tidak berarti; biaya salah jawab jauh lebih mahal.

Jangan memakai double untuk jawaban bilangan bulat. double hanya tepat sampai sekitar 15 digit berarti, jadi jawaban bulat besar bisa tercetak sebagai 1e+18. Pakai double hanya kalau soal memang meminta pecahan, dan selalu dengan fixed << setprecision(k) sesuai permintaan soal.

Soal menyebut berapa digit desimal yang diminta. Kalau soal meminta dua angka di belakang koma, mencetak 82.5 dinilai salah walaupun nilainya sama. fixed << setprecision(2) membuat 82.5 tercetak 82.50.

Soal yang meminta jawaban pecahan sering menyertakan kalimat "jawaban dianggap benar jika selisihnya dengan jawaban juri tidak lebih dari 10⁻⁶". Kalimat itu berarti pembulatan kecil dimaafkan — tetapi format keluarannya tetap harus sesuai. Baca bagian format keluaran sampai habis sebelum menentukan setprecision.

Ringkasan

  • Variabel adalah kotak berlabel; tipenya menentukan isi dan ukurannya.
  • int aman sampai sekitar ±2 × 10⁹; di atas itu pakai long long. Yang menentukan adalah besarnya hasil, bukan besarnya masukan.
  • / antara dua bilangan bulat membuang pecahannya; % memberi sisanya. n % 10 dan n / 10 adalah pasangan untuk memproses digit.
  • double tidak tepat; jangan dibandingkan dengan ==.
  • cin >> melewati semua spasi dan pindah baris, lalu membaca satu nilai.
  • Program untuk penilai membaca dari masukan standar dan mencetak hanya jawaban yang diminta.
  • fixed dan setprecision(k) dari <iomanip> menetapkan banyaknya digit di belakang koma.

Latihan bergaya OSN

Tiga soal berikut sudah memakai masukan standar, persis seperti soal sungguhan. Uji jawabanmu dengan mengalirkan masukan dari terminal:

echo "10 7 3" | ./ubin

Kerangka ketiganya ada di exercises/03/osn/.

Soal 1 — Ubin Lantai

Sebuah ruangan berbentuk persegi panjang berukuran P × L sentimeter akan dipasangi ubin berbentuk persegi dengan sisi S sentimeter. Ubin dipasang mulai dari salah satu sudut ruangan, sejajar dinding, dan tidak boleh dipotong. Sisa lantai di tepi ruangan yang tidak tertutup ubin utuh akan ditutup semen.

Hitung berapa ubin utuh yang terpasang dan berapa luas lantai yang harus ditutup semen.

Format masukan. Satu baris berisi tiga bilangan bulat P, L, dan S.

Format keluaran. Satu baris berisi dua bilangan bulat: banyaknya ubin utuh, lalu luas sisa lantai dalam sentimeter persegi, dipisahkan satu spasi.

Batasan.

  • 1 ≤ P, L ≤ 10⁹
  • 1 ≤ S ≤ 10⁹

Contoh masukan

10 7 3

Contoh keluaran

6 16

Penjelasan. Ke arah panjang muat 10 / 3 = 3 ubin, ke arah lebar muat 7 / 3 = 2 ubin, jadi seluruhnya 3 × 2 = 6 ubin. Luas ruangan 10 × 7 = 70, luas tertutup 6 × 3 × 3 = 54, sisanya 70 − 54 = 16.

Kerangkanya: exercises/03/osn/01-ubin-lantai.cpp

Soal 2 — Nilai Akhir

Nilai akhir satu mata pelajaran dihitung dari tiga komponen dengan bobot tetap: nilai tugas 30%, nilai UTS 30%, dan nilai UAS 40%.

Format masukan. Satu baris berisi tiga bilangan bulat: nilai tugas, nilai UTS, dan nilai UAS.

Format keluaran. Satu baris berisi nilai akhir dengan tepat dua digit di belakang koma.

Batasan. Ketiga nilai berada di antara 0 dan 100.

Contoh masukan

80 75 90

Contoh keluaran

82.50

Penjelasan. 0,3 × 80 + 0,3 × 75 + 0,4 × 90 = 24 + 22,5 + 36 = 82,5. Karena soal meminta dua digit di belakang koma, jawabannya ditulis 82.50, bukan 82.5.

Kerangkanya: exercises/03/osn/02-nilai-akhir.cpp

Soal 3 — Deret Aritmetika

Sebuah deret aritmetika dimulai dari suku pertama a dan setiap suku berikutnya bertambah b dari suku sebelumnya. Tentukan suku ke-n dan jumlah n suku pertamanya.

Format masukan. Satu baris berisi tiga bilangan bulat a, b, dan n.

Format keluaran. Satu baris berisi dua bilangan bulat: suku ke-n dan jumlah n suku pertama, dipisahkan satu spasi.

Batasan.

  • −10⁶ ≤ a, b ≤ 10⁶
  • 1 ≤ n ≤ 10⁶
  • Kerjakan tanpa perulangan.

Contoh masukan

3 5 4

Contoh keluaran

18 42

Penjelasan. Deretnya 3, 8, 13, 18. Suku keempatnya 18, dan 3 + 8 + 13 + 18 = 42.

Larangan memakai perulangan di soal ini bukan gaya soal OSN — di OSN yang dibatasi adalah waktu berjalan, bukan cara menulisnya. Larangan itu dipasang karena perulangan baru dibahas di Bab 05, dan karena rumus deret memang jawaban yang lebih baik: ia tetap cepat walaupun n dinaikkan menjadi 10¹⁸.

Kerangkanya: exercises/03/osn/03-deret-aritmetika.cpp

Bingung atau sudah selesai? Buka halaman pembahasan PR.