Pertimbangkan pertanyaan 1C putaran Google Code Jam berikut :
Tembok Besar Tiongkok dimulai sebagai garis tanpa batas, di mana ketinggian di semua lokasi adalah .
Sejumlah suku , , akan menyerang dinding sesuai dengan parameter berikut - hari awal, , mulai kekuatan , mulai koordinat barat, , dan mulai koordinat timur, . Serangan pertama ini terjadi pada hari , pada kisaran , dengan kekuatan . Jika ada bagian dari Tembok Besar dalam yang memiliki ketinggian , serangan itu berhasil, dan pada akhir hari, dinding akan dibangun sehingga setiap segmen itu dalam tinggi maka akan menjadi tinggi (atau lebih besar, jika beberapa serangan lain hari itu mengenai segmen yang sama dengan kekuatan )
Setiap suku akan melakukan hingga serangan sebelum mundur, dan setiap serangan akan ditentukan secara iteratif dari yang sebelumnya. Setiap suku memiliki beberapa , , dan yang menentukan urutan serangan mereka: Mereka akan menunggu hari antara serangan, mereka akan memindahkan unit serangan unit untuk setiap serangan (negatif = barat, positif = timur), meskipun ukuran kisaran akan tetap sama, dan kekuatan mereka juga akan meningkat / berkurang dengan nilai konstan setelah setiap serangan.
Tujuan dari masalah ini adalah, mengingat deskripsi lengkap dari suku-suku yang menyerang, menentukan berapa banyak dari serangan mereka akan berhasil.
Saya berhasil membuat kode solusi yang berfungsi, berjalan dalam sekitar 20 detik: Saya percaya solusi yang saya implementasikan membutuhkan waktu , di mana jumlah total serangan di simulasi (maks. 1000000 ), dan X = jumlah total titik tepi unik pada rentang serangan (maks. 2000000 ).A =
Pada tingkat tinggi, solusi saya:
- Membaca semua informasi Suku
- Menghitung semua koordinat unik untuk rentang serangan - O ( A )
- Merupakan Wall sebagai pohon biner yang diperbarui secara malas pada rentang yang melacak nilai ketinggian minimum. Daun adalah rentang dua koordinat X dengan tidak ada di antaranya, dan semua simpul orangtua mewakili interval kontinu yang dicakup oleh anak-anak mereka. - O ( X log X )
- Hasilkan semua Serangan yang akan dilakukan oleh setiap Suku, dan urutkan berdasarkan hari -
- Untuk setiap serangan, lihat apakah itu akan berhasil ( waktu permintaan). Ketika hari berubah, ulangi semua serangan sukses yang belum diproses dan perbarui tembok yang sesuai ( log waktu pembaruan X untuk setiap serangan). - O ( A log X )
Pertanyaan saya adalah ini: Apakah ada cara untuk berbuat lebih baik daripada ? Mungkin, adakah cara strategis untuk mengambil keuntungan dari sifat linear dari serangan berturut-turut Suku? 20 detik terasa terlalu lama untuk solusi yang dimaksudkan (meskipun Jawa mungkin yang harus disalahkan untuk itu).
sumber
Jawaban:
Ruang yang jelas untuk perbaikan adalah langkah ini:
Kita tahu bahwa suku-suku akan menyerang dari hari tertentu, secara berkala. Itu berarti kita pada dasarnya harus menggabungkan banyak daftar yang sudah disortir. Pernyataan masalah juga memberi tahu kita bahwa tidak akan ada lebih dari 1.000 suku (yaitu 1.000 daftar untuk digabung); jumlah kecil dibandingkan dengan serangan maksimum 1.000.000! Bergantung pada waktu relatif implementasi Anda, beralih ini bisa memotong waktu pemrosesan menjadi dua.
Hanya itu yang bisa saya sarankan untuk mengoptimalkan kompleksitas teoretis, tetapi saya tidak punya bukti bahwa itu akan optimal setelah perubahan ini.
Saya mencoba puzzle sendiri, tetapi saya menggunakan banyak representasi dinding: sebuah pohon pencarian biner (
std::map
tepatnya C ++ ) menyimpan lokasi di mana ketinggian dinding berubah. Dengan ini, saya dapat menambah dan menghapus node sesuai kebutuhan (yaitu jika bagian yang rumit terkena serangan besar, luar biasa, atau beberapa serangan dengan kekuatan yang sama menyentuh, jumlah node akan berkurang secara signifikan). Ini memecahkan input besar dalam 3,9 detik (pada laptop pengembangan mid-spec saya). Saya menduga ada beberapa alasan untuk perbaikan:Saya tidak menggunakan pra-pemrosesan (tahapan ditentukan dengan melacak kapan setiap suku akan menyerang berikutnya, dan mencari gabungan terendah setiap belokan). Sekali lagi ini secara teori lebih buruk, tetapi untuk sebagian besar kasus saya menduga overhead yang lebih rendah berarti lebih cepat (saya akan menguji ini dan kembali kepada Anda).Pembaruan : Saya menambahkan antrian prioritas untuk serangan, mirip dengan metode yang dijelaskan di atas (walaupun alih-alih membuat array besar saya menghitungnya sambil jalan) dan melihat waktu berkurang menjadi 3,0 detik untuk input besar.Singkatnya, sementara saya pikir algoritma Anda hampir-optimal dalam kasus umum, ada beberapa cara Anda bisa mempercepatnya untuk input khas .
sumber
Berikut ini dihapus dari pertanyaan, karena itu adalah jawaban.
Melihat diskusi lain dan solusi yang berhasil tampaknya menunjukkan bahwa solusi yang saya jelaskan adalah algoritma yang diharapkan. Perlambatan dalam solusi saya mungkin hanya karena malas menggunakan auto-boxing dan struktur pohon berbasis pointer, daripada yang berbasis array - jadi saya menduga bahwa, jika suatu solusi memang ada, itu mungkin bukan keseluruhan jauh lebih baik daripada apa yang ada di sini.
Solusinya dapat ditemukan di sini . Ini sangat mirip dengan apa yang saya posting di sini; jadi saya lebih cenderung percaya bahwa solusi yang lebih efisien tidak ada.
sumber