Pembahasan Soal Bebek, Kartu, Paste, Labirin (Harry Potter)

Embed Size (px)

DESCRIPTION

Pembahasan Soal Bebek, Kartu, Paste, Labirin (Harry Potter)

Citation preview

PELATNAS : 4 Besar Terpilih

Pembahasan Soal bebek

Soal ini sepintas nampaknya rumit sekali tetapi dengan menganalisanya terlebih dahulu maka anda akan menemukan soal yang jauh lebih sederhana.

Pertama kita bahas dahulu submasalah pengurutan bebek satu varietas, i.e."jika ada n bilangan acak yang masing-masing unik, berapa banyak pemindahan minimum yang perlu dilakukan untuk mengurutkannya".

Ide dari pemecahan masalah ini adalah: mencari berapa banyak di antara bilangan-bilangan itu yang sudah dalam pengurutan. Ada sejumlah cara pengurutan, oleh pada setiap bilangan kita hitung suatu indeks keterurutan (IK) yang didefinisikan sbb.

IK suatu bilangan adalah adalah jumlah bilangan disebelah kirinya yang lebih kecil darinya namun berada dalam keterurutan termasuk bilangan itu sendiri.

Perhatikan bilangan-bilangan ini: 1, 3, 2, 7, 4, 6, 5

Indeks-indeks keterurutan diperoleh sbb (dituliskan di dalam tanda kurung), 1(1), 3(2), 2(2), 7(3), 4(3), 6(4), 5(4)

Berapa banyak bilangan yang tidak dalam keterurutan adalah banyaknya bilangan dikurangi oleh IK maksimum. Untuk contoh tersebut maka IK maksimum adalah 4 dan ada 7 bilangan sehingga jumlah pemindahan (yang dilakukan pada bilangan yang tidak berada dalam urutan tersebut) adalah 7 -4 = 3.

Sekarang jika kita lakukan pada bilangan-bilangan ini: 1, 2, 3, 4, 5, 6, 7 Maka diperoleh 1(1), 2(2), 3(3), 4(4), 5(5), 6(6), 7(7) dan jumlah pemindahan adalah = 7 - 7 = 0.

Kita coba lagi pada bilangan-bilangan ini: 7, 6, 5, 4, 3, 2, 1 Maka diperoleh 7(1), 6(1), 5(1), 4(1), 3(1), 2(1), 1(1)dan jumlah pemindahan adalah = 7 - 1 = 6.

Beruntunglah soal ini tidak meminta cara-cara pemindahannya karena untuk contoh di atas akan terdapat banyak kemungkinan yang dapat dilakukan untuk 3 kali pemindahan tesebut. Itu terlihat dari adanya indeks yang sama seperti bilangan 3 dan 2, bisa 2 maupun 3 yang dipindahkan.

Ok, jadi bagaimana algoritmanya? Saat menghitung IK untuk X[j] kita cukupmenemukan bilangan yang lebih kecil darinya dengan IK terbesar, misalnya X[k], dan menghitung IK[j] := IK[k] + 1

for j := 1 to N do begin

IKtmp := 0;

for k := 1 to j-1 do

if (X[k] < X[j] and IK[k] > IKtmp) then

IKtmp := IK[k];

IK[j] = IKtmp + 1;

end;

Sekarang bagaimana dengan varietas lebih dari satu? Gampang, karena soal membatasi jumlah varietas hanya 4 maka pertama kita permutasikan urutan varietas, lalu memberi tambahan bobot sesuai dengan urutan varietasnya. Misalnya untuk urutan varietas v1, v2, v3, v4 maka bobot setiap bebek dari v2 di tambah 100, dari v3 di tambah 200, di v4 di tambah 300 (mengapa kelipatan 100? Karena bobot maksimum bebek 100!). Kemudian untuk satu permutasi dilakukan penghitungan IK sepertinya hanya satu varietas saja.

Untuk jumlahvarietas = 4 maka terdapat 24 permutasi, berati pula ada 24 kali penghitungan IK dan masing-masing diperoleh jumlah pemindahan yang perlu dilakukan. Dari ke 24 jumlah pemindahan untuk masing-masing permutasi diambil jumlah yang paling minimum.

Lalu, bagaimanakah melakukan permutasi? Silahkan membaca lagi bahan PJJ yang lalu....

Pembahasan Soal Kartu

Soal ini dibuat bukan untuk level IOI karena batasan data masih memungkinkan cara-cara konvensional tanpa perlu adanya trick-trick canggih. Singkatnya, solusinya dapat dibuat secara naif dan sederhana sbb.

Pertama anda memerlukan suatu array untuk menyimpan urutan kartu-kartu tsb dan kita sebut array X, yang berindeks 0, 1, 2, ..., N-1. Untuk memudahkan algoritma maka kita nomori ulang setiap kartu dengan mengurangi 1 pada setiap angka. Jadi X[i] array mula-mula berisi kartu i.

Jika jumlah baris adalah k dimana k=N/3, deal demi deal isi array akan mengalami pemindahan isi sbb. Kartu yang ada di X[0] tetap pada x[0], kartu di X[1] pindah ke X[k], kartu di X[2] pindah ke X[2k], kartu di X[3] pindah ke X[1], kartu di X[4] pindah ke X[k+1], kartu di X[5] pindah ke X[2k+1], kartu di X[6] pindah ke X[2], kartu di X[7] pindah ke X[k+2]... dst

sehingga secara umum kartu di X[j] akan pindah ke X[(j mod 3) * k + j div 3].

Dalam implementasinya tentu saja operasinya dilakukan pada array yang berbeda sbb.

for j := 0 to N-1 do

Y[(j mod 3) * k + j div 3] := X[j];

X := Y;

Pada setiap deal nomor kolom dimana kartu yang diambil tsb berada disebutkan, yaitu kolom c, dan kartu-kartu pada kolom c tsb ditandai. Untuk penandaannya ada yang menggunakan flag boolean, yaitu dengan menandai false kartu-kartu yang tidak berada di kolom c (inisialisasi semua true). Kartu-kartu i dengan flag[i] =true semakin lama semakin sedikit dan akhirnya tersisa kartu yang mungkin.

Cara tersebut memerlukan iterasi sebanyak jumlah kartu. Cara lain yang lebih efisien adalah menggunakan counter. Sebelum deal-deal dilakukan, setiap count[j] diinisialisasi 0. Ketika masukan adalah kolom c (c =1, 2, atau 3) maka counter dari setiap kartu pada kolom tsb bertambah 1. Dalam Pascal:

for i := 0 to k-1 do

inc(count[X[(c-1)+i*3]]);

yang hanya melakukan N/3 iterasi saja. Cara ini akan semakin lebih efisien jika dalam soal jumlah kolom besar.

Untuk soal standart IOI, biasanya ukuran data di paskan ke ukuran maksimum memori yang bisa digunakan sehingga cara-cara naif seperti ini masih perlu diefisienkan lagi.

Misalnya operasi deal kartu hanya dilakukan pada array X saja. Misalnya kita mulai dari X[1] dipindahkan ke X[k], X[k] dipindahkan ke X[(k mod 3)*k + k div 3], dst.

Jika jumlah deal adalah ITER maka kartu-kartu yang mungkin adalah kartu-kartu j dengan COUNT[j] = ITER.

first := true;

for j := 0 to N-1 do begin

if (COUNT[j] = ITER) begin

if not first then begin

write(' ');

first := false

end;

write(j+1) {penomoran kartu dikembalikan ke urutan dari 1, 2, }

end

end;

Pembahasan Soal Paste

Tanpa menganalisis soal ini secara hati-hati mungkin anda langsung berfikir perlunya suatu struktur data untuk menyimpan array untuk mencatat posisi baris-baris selama operasi-operasi itu dilakukan. Itu masih OK kalau N masih kecil. Kalau kebetulan dalam testcase digunakan N = 100000 maka anda akan menghadapi masalah memory untuk mengalokasi array sebesar itu.

Dengan membacanya dan menganalisisnya dengan lebih hati-hati maka anda akan menemukan kesederhanaan masalah ini.

Pertama, operasi cut-and-paste itu bersifat reversible. Artinya, dari hasil suatu operasi kita bisa cari asalnya sebelum operasi itu dilakukan. perhatikan contoh berikut ini (baris-baris ditulis ke kanan dengan pemisah tanda koma).

Misalnya sebelum operasi baris-baris berisi:a, b, c, d, e, f, g, h, i, jsetelah operasi cut(4, 8), maka akan dihasilkan baris-barisa, b, c, i, jdengan (d, e, f, g, h) di dalam 'buffer'. Setelah paste(2) dihasilkan baris-barisa, b, d, e, f, g, h, c, i, j

Sekarang kita balikan (reversi) operasinya.

Operasi paste dilakukan setelah posisi ke 2. Jadi posisi-posisi 1 - 2 (yaitu a, b) setelah dan sebelum paste sama. Kemudian karena operasi cut dilakukan pada 5 baris maka sejak posisi ke 3 - 7 (yaitu d, e, f, g, h) adalah berasal dari hasil paste isi buffer. Dan, posisi ke 8 - 10 (yaitu c, i, j) mengalami pergeseran sebanyak 5 posisi ke kiri; jadi sebelum paste ada di posisi ke 3-5. Jadi sebelum paste adalah a, b, c, i, j. Sementara, d, e, f, g, ada di 'buffer'.

Operasi cut(4,8) tidak mempengaruhi posisi 1 - 3 karena dilakukan mulai baris ke 4. Berarti terjadi pergeseran baris ke 4 - 5 (yaitu i, j) sebanyak 5 posisi ke kanan. Di tengah dimasukan yang ada di buffer.

Secara umum jika ditanyakan berasal dari mana baris yang sekarang bernomor x sebelum operasi cut-and-paste (M, A, S). Berikut ini kita formulasikan perncarian harga z yaitu nomor baris dari baris x berasal. Maka, terdapat sejumlah kemungkinan kasus:

Reversi Paste mencari y dari x

jika x (S+A-M+1) maka sebelum paste x mengalami pergeseran sebanyak (A-M+1) baris, yaitu y = x+A-M+1

jika x > S and x = M maka sebelum cut ada di sebelah kanannya, yaitu di z = y+A-M+1

Jika dilakukan sejumlah operasi, maka untuk suatu harga x reversi operasi tsb dilakukan dari yang terakhir hingga yang pertama. Harga z dari operasi ke p menjadi x dari operasi ke p-1, secara berulang. Karena x hanya diminta untuk 10 baris pertama saja maka algoritma ini anda hanya melakukan reversi sebanyak 10 x jumlah operasi.

Nah, anda sekarang hanya perlu suatu array (berukuran 1000 x 3 integer) untuk menyimpan seluruh parameter operasi tsb dan sementara buffer' itu sebenarnya tidak ada! (Lho koq bisa??) LABIRIN

Harry Potter sedang terjebak dalam suatu labirin (labirynth) bawah tanah yang berisi bom waktu. Dalam labirin terdapat sejumlah ruangan dengan koridor-koridor yang menghubungkannya. Setiap koridor hanya menghubungkan tepat dua ruangan yang berbeda dan setiap pasang ruangan oleh satu koridor atau tidak sama sekali. Ruang-ruang itu bisa berada dalam salah satu dari dua status: terkunci atau tak terkunci. Seseorang tidak bisa masuk ke dalam ruang yang tekunci, tapi ia bisa keluar dari ruangan yang terkunci itu. Sementara seseorang bisa keluar masuk ruang tak terkunci.

Di beberapa ruang terdapat tombol yang apabila ditekan akan mengubah status sejumlah ruang. Misalnya, ruang yang terkunci menjadi tidak terkunci dan sebaliknya ruang yang tak terkunci menjadi terkunci. Tentu saja jika Harry Potter berada di dalam ruangan yang memiliki tombol, ia tidak harus menekannya. Ia baru menekan manakala ia memerlukannya.

Salah satu ruang terdapat tangga untuk naik ke atas. Tuliskan suatu program dengan nama LABIRIN.PAS yang dapat membantu Harry Potter yang berada dalam salah satu ruang menemukan ruang yang berisi tangga naik ke atas itu termasuk tombol-tombol mana yang harus ditekan. Karena dikejar waktu program harus menemukan jumlah koridor yang harus dilalui Harry Potter, yang mungkin disertai dengan menekan tombol-tombol di ruangan tertentu ia sempat berada.

Masukan berisi deskripsi ruangan serta koridor-koridor dalam labirin tersebut, ruangan saat awal Harry Potter berada, dan daftar sejumlah tombol serta masing-masing ruangan yang statusnya berubah jika tombol tersebut ditekan.

Format Masukan

Masukan adalah file teks bernama LABIRIN.IN dengan format sebagai berikut. Baris pertama dari file masukan berisi dua integer yaitu N, yang menyatakan jumlah total kamar (1 N 100), dan S, yang menyatakan jumlah tombol (jumlah kamar yang berisikan tombol) (1 S 8). Tombol-tombol terlekat pad kamar-kamar bernomor 1 sampai S.

N baris berikutnya berisi deskripsi setiap kamar. Nomor kamar i pada baris ke (i+1). Pada setiap baris tersebut jika dimulai dengan 0 menandakan bahwa pada mulanya kamar tbs tak terkunci atau jika dimulai dengan 1 menandakan pada mulanya kamar ybs terkunci. Setelah spasi terdapat bilangan integer K, yang menyatakan jumlah kamar yang berhubungan dengan kamar ybs melalui koridor-koridor. Masih dalam baris yang sama, setelah satu spasi setelah terdapat K bilangan yang menyatakan nomor-nomor kamar tsb dengan penulisan yang dipisahkan spasi.

Dalam S baris berikutnya terdapat tombol-tombol, dari kamar pertama hingga kamar ke S.

Setiap dari baris-baris tsb dimulai dengan bilangan integer L, yang menyatakan jumlah kamar yang akan berubah jika tombol ditekan. Setelah spasi, berikutnya adalah L integer yang masing-masing menyatakan nomor-nomor kamar ybs yang masng-masing terpisahkan satu spasi.

Baris terakhir berisi dua bilangan A dan B; A adalah nomor kamar Harry Potter saat ini berada dan B nomor kamar yang berisi tangga naik ke atas.

Format Keluaran

Keluaran adalah file LABIRIN.OUT yang hanya berisi satu bilangan integer yang menyatakan jumlah minimal koridor yang harus dilalui untuk mencapai ruangan bertangga.

Note: Setiap data test dijamin selalu memiliki solusi, artinya dari ruang A selalu ada jalan untuk mencapai ruang B.

Contoh

LABIRIN.IN

4 1

0 1 3

1 2 3 4

0 2 1 2

0 1 2

1 2

3 4

LABIRIN.OUT

4

LABIRIN.IN

5 2

0 2 2 5

1 2 1 3

0 2 2 4

1 2 3 5

0 2 1 4

2 2 4

2 3 4

5 3

LABIRIN.OUT

3

LABIRIN.IN

6 2

0 2 6 5

1 2 4 6

0 1 4

1 3 2 5 3

0 3 1 4 6

0 3 1 5 2

3 2 5 3

1 4

6 3

LABIRIN.OUT

8