Apakah ada solusi yang lebih cepat untuk Masalah Tembok Besar Google Code Jam


16

Pertimbangkan pertanyaan 1C putaran Google Code Jam berikut :

Tembok Besar Tiongkok dimulai sebagai garis tanpa batas, di mana ketinggian di semua lokasi adalah .0

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 tinggiNN1000DSWED[W,E]S[W,E]<S[W,E]<SS (atau lebih besar, jika beberapa serangan lain hari itu mengenai segmen yang sama dengan kekuatan )S>S

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.1000δDδXδSδD1δX

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 =O(AlogA+(A+X)logX)A=1000000X=2000000

Pada tingkat tinggi, solusi saya:

  • Membaca semua informasi Suku
  • Menghitung semua koordinat unik untuk rentang serangan - O ( A )XO(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 )XXO(XlogX)
  • Hasilkan semua Serangan yang akan dilakukan oleh setiap Suku, dan urutkan berdasarkan hari - O(AlogA)
  • 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 )logXlogXO(AlogX)

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).O(AlogA+(A+X)logX)


3
Tolong jangan ditutup. Itu adalah pertanyaan yang valid. Sebuah jawaban akan menjadi bukti batas bawah, menunjukkan bahwa kita tidak dapat melakukan yang lebih baik, jika itu memang yang terbaik yang bisa kita lakukan. Sebagai contoh, saya kira kita mungkin bisa menggunakan Elemen Perbedaan Elemen di sini, tetapi belum menemukan waktu untuk memikirkannya.
Aryabhata

Kalau begitu aku akan tetap buka :)
torquestomp

Jawaban:


2

Ruang yang jelas untuk perbaikan adalah langkah ini:

Hasilkan semua Serangan yang akan dilakukan oleh setiap Suku, dan urutkan berdasarkan hari - O(AlogA)

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::maptepatnya 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:

  • Seperti yang Anda tunjukkan, tinju dan unboxing bisa menjadi mahal, tetapi wadah berbasis template C ++ menghindari ini sepenuhnya.
  • Sementara representasi dinding yang saya gunakan secara teoritis lebih buruk, dalam sebagian besar kasus mampu secara dinamis mengurangi jumlah node membuatnya super cepat (sebagian besar kasus uji max-out di bawah 1k node, dan semua kecuali 2 berada di bawah 10k) . Faktanya, satu-satunya kasus yang memakan waktu signifikan adalah # 7, yang tampaknya telah menguji banyak rentang non-berpotongan.
  • 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 .


1

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.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.