Saya telah berlatih untuk kompetisi pemrograman yang akan datang dan saya telah menemukan sebuah pertanyaan yang membuat saya benar-benar bingung. Namun, saya merasa seolah-olah itu adalah konsep yang harus saya pelajari sekarang daripada berharap itu tidak pernah muncul.
Pada dasarnya, ini berhubungan dengan bidak ksatria di papan catur. Anda diberikan dua masukan: lokasi awal dan lokasi akhir. Tujuannya adalah untuk menghitung dan mencetak jalur terpendek yang dapat diambil ksatria untuk mencapai lokasi target.
Saya tidak pernah berurusan dengan hal-hal yang membutuhkan jalur terpendek, dan saya bahkan tidak tahu harus mulai dari mana. Logika apa yang saya terapkan untuk mengatasi masalah ini?
PS Jika ada relevansinya, mereka ingin Anda menambah gerakan normal ksatria dengan juga membiarkannya bergerak ke empat sudut alun-alun yang dibentuk oleh (berpotensi) delapan gerakan yang dapat dilakukan seorang ksatria, mengingat bahwa pusat alun-alun adalah lokasi ksatria.
sumber
Jawaban:
Anda memiliki grafik di sini, di mana semua gerakan yang tersedia terhubung (nilai = 1), dan gerakan yang tidak tersedia terputus (nilai = 0), matriks renggang akan seperti:
Dan jalur terpendek dari dua titik dalam grafik dapat ditemukan menggunakan http://en.wikipedia.org/wiki/Dijkstra's_algorithm
Kode semu dari wikipedia-page:
EDIT:
Introduction to Algorithms
ISBN 0-262-03384-4. Atau Anda dapat mencoba wikipedia, http://en.wikipedia.org/wiki/List_of_algorithmssumber
EDIT: Lihat jawaban Simon , di mana dia memperbaiki rumus yang disajikan di sini.
Sebenarnya ada rumus O (1)
Ini adalah gambar yang saya buat untuk memvisualisasikannya (Kotak yang bisa dijangkau oleh seorang ksatria pada gerakan ke N dilukis dengan warna yang sama).
Dapatkah Anda memperhatikan polanya di sini?
Meskipun kita dapat melihat polanya, sangat sulit untuk menemukan fungsi
f( x , y )
yang mengembalikan jumlah gerakan yang diperlukan untuk berpindah dari kuadrat( 0 , 0 )
ke kuadrat( x , y )
Tapi inilah rumus yang bekerja kapan
0 <= y <= x
Catatan: Pertanyaan ini ditanyakan pada SACO 2007 Hari 1
Dan solusinya ada di sini
sumber
2 * floor((y - delta) / 3) + delta
dandelta - 2 * floor((delta - y) / 4)
. Ini adalah solusi resmi dari halaman kontes ini, tetapi itu salah. Persamaan pertama ini (dariif
) mengembalikan jawaban yang salah. Di papan catur [-1000..1000] x [-1000..1000], yang berukuran besar 2001x2001 (tetapi secara logika tidak terbatas), jawaban yang diberikan menghitung 2.669.329 dari 4.004.001 bidang benar (66,66%). Ada yang tahu solusi yang berfungsi tanpa loop apa pun?Berikut adalah solusi O (1) yang benar, tetapi untuk kasus di mana kesatria itu bergerak seperti seorang ksatria catur saja, dan pada papan catur yang tak terbatas:
https://jsfiddle.net/graemian/5qgvr1ba/11/
Kunci untuk menemukannya adalah dengan memperhatikan pola yang muncul saat Anda menggambar papan. Pada diagram di bawah ini, bilangan dalam persegi adalah jumlah gerakan minimum yang diperlukan untuk mencapai persegi tersebut (Anda dapat menggunakan pencarian luas-pertama untuk menemukan ini):
Karena penyelesaiannya simetris pada sumbu dan diagonal, saya hanya menggambar case x> = 0 dan y> = x.
Blok kiri bawah adalah posisi awal dan angka di blok mewakili jumlah minimum gerakan untuk mencapai blok tersebut.
Ada 3 pola yang perlu diperhatikan:
(Pastikan Anda melihat kedua set diagonal sebagai kiri atas ke kanan bawah. Mereka memiliki jumlah gerak yang konstan. Diagonal kiri bawah kiri atas jauh lebih kompleks.)
Anda bisa mendapatkan rumus untuk masing-masing. Blok kuning adalah kasus khusus. Jadi solusinya menjadi:
dengan yang paling sulit menjadi grup vertikal:
Lihat biola untuk kasus lainnya.
Mungkin ada pola yang lebih sederhana atau lebih elegan yang saya lewatkan? Jika demikian, saya ingin sekali melihat mereka. Secara khusus, saya memperhatikan beberapa pola diagonal pada kotak vertikal biru, tetapi saya belum menjelajahinya. Terlepas dari itu, solusi ini masih memenuhi batasan O (1).
sumber
Masalah yang sangat menarik yang saya temui baru-baru ini. Setelah mencari beberapa solusi, saya mencoba memulihkan rumus analitik (
O(1) time and space complexity
) yang diberikan pada solusi SACO 2007 Hari 1 .Pertama-tama saya ingin mengapresiasi Graeme Pyle untuk visualisasi yang sangat bagus yang membantu saya memperbaiki formula.
Entah kenapa (mungkin untuk penyederhanaan atau keindahan atau hanya karena kesalahan) mereka pindah
minus
sign kefloor
operator, akibatnya formula salahfloor(-a) != -floor(a) for any a
.Berikut rumus analitik yang benar:
Rumus ini berfungsi untuk semua pasangan (x, y) (setelah menerapkan sumbu dan simetri diagonal) kecuali (1,0) dan (2,2) kasus sudut, yang tidak memenuhi pola dan hardcode dalam cuplikan berikut:
Catatan: jQuery hanya digunakan untuk ilustrasi, untuk
distance
fungsi kode lihat .sumber
Ya, Dijkstra dan BFS akan memberi Anda jawabannya, tetapi menurut saya konteks catur dari masalah ini memberikan pengetahuan yang dapat menghasilkan solusi yang jauh lebih cepat daripada algoritma jalur terpendek generik, terutama pada papan catur tak terbatas.
Untuk mempermudah, mari kita gambarkan papan catur sebagai bidang (x, y). Tujuannya adalah untuk menemukan jalur terpendek dari (x0, y0) ke (x1, y1) hanya menggunakan langkah kandidat (+ -1, + -2), (+ -2, + -1), dan (+ -2 , + -2), seperti yang dijelaskan dalam PS pertanyaan
Berikut adalah pengamatan baru: gambar persegi dengan sudut (x-4, y-4), (x-4, y + 4), (x + 4, y-4), (x + 4, y + 4) . Set ini (sebut saja S4) berisi 32 poin. Jalur terpendek dari 32 titik ini ke (x, y) membutuhkan tepat dua langkah .
Jalur terpendek dari salah satu dari 24 titik dalam set S3 (didefinisikan serupa) ke (x, y) membutuhkan setidaknya dua gerakan .
Oleh karena itu, jika | x1-x0 |> 4 atau | y1-y0 |> 4, jalur terpendek dari (x0, y0) ke (x1, y1) tepat dua langkah lebih besar dari jalur terpendek dari (x0, y0) ke S4. Dan masalah terakhir dapat diselesaikan dengan cepat dengan iterasi langsung.
Misal N = max (| x1-x0 |, | y1-y0 |). Jika N> = 4, maka jalur terpendek dari (x0, y0) ke (x1, y1) memiliki langkah ceil (N / 2) .
sumber
Jawaban O (1) di atas [ https://stackoverflow.com/a/8778592/4288232 oleh Mustafa Serdar Şanlı] tidak benar-benar berfungsi. (Periksa (1,1) atau (3,2) atau (4,4), selain untuk kasus tepi yang jelas dari (1,0) atau (2,2)).
Di bawah ini adalah solusi yang jauh lebih buruk (python), yang berfungsi (dengan tambahan "tes"):
sumber
above
ataubelow
tidak benar-benar berfungsi pada SO.Yang perlu Anda lakukan adalah memikirkan kemungkinan gerakan ksatria sebagai grafik, di mana setiap posisi di papan adalah simpul dan kemungkinan pindah ke posisi lain sebagai tepi. Tidak perlu algoritma dijkstra, karena setiap sisi memiliki bobot atau jarak yang sama (semuanya mudah atau pendek untuk dilakukan). Anda tinggal melakukan pencarian BFS dari titik awal Anda sampai Anda mencapai posisi akhir.
sumber
Solusi dari prinsip pertama dengan Python
Saya pertama kali mengalami masalah ini dalam tes Codility. Mereka memberi saya waktu 30 menit untuk menyelesaikannya - saya butuh waktu lebih lama dari itu untuk mendapatkan hasil ini! Masalahnya adalah: berapa banyak gerakan yang dibutuhkan seorang ksatria untuk beralih dari 0,0 ke x, y hanya menggunakan jurus Knight yang sah. x dan y kurang lebih tidak dibatasi (jadi kita tidak berbicara di sini tentang papan catur 8x8 sederhana).
Mereka menginginkan solusi O (1). Saya menginginkan solusi di mana program secara jelas menyelesaikan masalah (yaitu saya menginginkan sesuatu yang lebih jelas benar daripada pola Graeme - pola memiliki kebiasaan rusak di tempat yang tidak Anda lihat), dan saya benar-benar ingin tidak harus bergantung pada formula yang tidak diperdebatkan, seperti dalam solusi Mustafa
Jadi, inilah solusi saya, untuk apa nilainya. Mulailah, seperti yang dilakukan orang lain, dengan mencatat solusinya simetris terhadap sumbu dan diagonal, jadi kita hanya perlu menyelesaikan untuk 0> = y> = x. Untuk kesederhanaan penjelasan (dan kode) saya akan membalikkan masalah: ksatria mulai dari x, y, dan membidik 0,0.
Misalkan kita mengecilkan masalah ke sekitar asalnya. Kita akan membahas apa sebenarnya arti 'vicinty' pada waktunya, tetapi untuk saat ini, mari kita tuliskan beberapa solusi di lembar contekan (asal di kiri bawah):
Jadi, dengan x, y pada grid, kita bisa membaca jumlah perpindahan ke titik awal.
Jika kita sudah mulai di luar grid, kita harus bekerja kembali ke sana. Kami memperkenalkan 'garis tengah', yang merupakan garis yang diwakili oleh y = x / 2. Ksatria mana pun di x, y pada baris itu dapat bekerja kembali ke lembar contekan menggunakan serangkaian gerakan jam 8 (yaitu: (-2, -1) gerakan). Jika x, y berada di atas garis tengah, maka kita membutuhkan urutan jam 8 dan jam 7, dan jika berada di bawah garis tengah, kita membutuhkan urutan jam 8 dan jam 10. 'jam bergerak. Dua hal yang perlu diperhatikan di sini:
Jadi, mari kita lihat pergerakan garis tengah atas. Apa yang kami klaim adalah:
(dx; dy) = (2,1; 1,2) (n8; n7) (notasi matriks, tanpa penyusunan huruf matematika - vektor kolom (dx; dy) sama dengan matriks persegi dikalikan dengan vektor kolom (n8; n7) - the jumlah jam 8 bergerak dan jumlah jam 7 bergerak), dan demikian pula;
(dx; dy) = (2,2; 1, -1) (n8; n10)
Saya mengklaim bahwa dx, dy akan menjadi kasar (x, y), jadi (x-dx, y-dy) akan berada di sekitar asal ('sekitar' apa pun yang ternyata).
Dua baris dalam kode yang menghitung istilah-istilah ini adalah solusi untuk ini, tetapi mereka dipilih untuk memiliki beberapa properti yang berguna:
(Apakah Anda ingin bukti ini?) Jadi, jarak Knight adalah jumlah dari n7, n8, n10 dan cheatsheet [x-dx, y-dy], dan cheatsheet kami berkurang menjadi ini:
Sekarang, ini bukanlah akhir dari cerita. Lihat 3 di baris bawah. Satu-satunya cara kita dapat mencapai ini adalah:
Ada pengoptimalan serupa yang bisa didapat dengan 4 di kanan atas. Selain memulai dari sana, satu-satunya cara untuk mencapainya adalah dengan gerakan jam 8 dari (4,3). Itu bukan di cheatsheet, tapi kalau ada di sana, jaraknya jadi 3, karena kita bisa saja jam 7 sampai (3,1) malah, yang jaraknya hanya 2. Jadi, kita harus back-track satu Pindah jam 8, lalu maju satu jam 7.
Jadi, kita perlu menambahkan satu angka lagi ke lembar contekan:
(Catatan: ada banyak optimisasi pelacakan mundur dari (0,1) dan (0,2) tetapi karena pemecah tidak akan pernah membawa kita ke sana, kita tidak perlu mengkhawatirkannya.)
Jadi di sini, kemudian, adalah beberapa kode Python untuk mengevaluasi ini:
Kebetulan, jika Anda ingin mengetahui rute yang sebenarnya, maka algoritma ini juga menyediakannya: ini hanyalah rangkaian gerakan jam 7 n7, diikuti oleh (atau diselingi dengan) gerakan jam 8 n8, n10 10- jam bergerak, dan tarian apa pun yang ditentukan oleh lembar contekan (yang, dengan sendirinya, bisa ada di lembar contekan).
Sekarang: Bagaimana membuktikan bahwa ini benar. Tidaklah cukup hanya membandingkan hasil ini dengan tabel jawaban yang benar, karena masalahnya sendiri tidak terbatas. Tetapi kita dapat mengatakan bahwa, jika jarak Knight dari bujur sangkar s adalah d, maka jika {m} adalah himpunan langkah legal dari s, jarak Knight dari (s + m) haruslah d-1 atau d + 1 untuk semua m. (Apakah Anda memerlukan bukti tentang ini?) Selanjutnya, harus ada setidaknya satu persegi yang jaraknya d-1, kecuali s adalah asalnya. Jadi, kita bisa membuktikan kebenarannya dengan menunjukkan properti ini berlaku untuk setiap persegi. Jadi:
Sebagai alternatif, kita dapat membuktikan kebenaran dari salah satu persegi dengan mengejar rute dari menuruni bukit ke asal. Pertama, periksa kewajaran s seperti di atas, lalu pilih s + m apa saja sehingga jarak (s + m) == d-1. Ulangi sampai kita mencapai asalnya.
Howzat?
sumber
Saya belum mempelajari grafik .. sesuai masalah penerapannya hanya melalui array, saya tidak bisa mendapatkan solusi lain selain ini. Saya memperlakukan posisi bukan sebagai peringkat dan file (Notasi catur biasa), tetapi sebagai indeks array. FYI, ini hanya untuk papan catur 8 * 8. Setiap saran perbaikan selalu disambut baik.
* Komentar harus cukup untuk pemahaman Anda tentang logika. Namun, Anda mungkin selalu bertanya.
* Dicentang pada DEV-C ++ 4.9.9.2 compiler (Bloodshed Software).
sumber
Saya pikir ini juga dapat membantu Anda ..
dan menggunakan Pemrograman Dinamis untuk mendapatkan solusinya.
PS: Ini agak menggunakan BFS tanpa harus bersusah payah mendeklarasikan node dan tepi grafik.
sumber
Berikut adalah solusi untuk masalah khusus ini yang diterapkan di Perl. Ini akan menampilkan salah satu jalur terpendek - mungkin ada lebih dari satu jalur dalam beberapa kasus.
Saya tidak menggunakan algoritma apa pun yang dijelaskan di atas - tetapi alangkah baiknya membandingkannya dengan solusi lain.
sumber
sumber
Hanya kode ruby dari jawaban jsfiddle Graeme Pyle di atas , menghapus semua kode tambahan dan mengubahnya menjadi ruby hanya untuk mendapatkan solusi dengan algoritmanya, sepertinya berfungsi. Masih menguji:
Satu-satunya niat adalah untuk menghemat waktu seseorang mengonversi kode jika ada yang membutuhkan kode lengkap.
sumber
inilah versi PHP dari fungsi Jules May
sumber
Ini program saya. Ini bukanlah solusi yang sempurna. Ada banyak perubahan yang harus dilakukan dalam fungsi rekursi. Tapi hasil akhir ini sempurna. Saya mencoba sedikit mengoptimalkan.
sumber
Berikut adalah versi C berdasarkan kode Mustafa Serdar Şanlı yang berfungsi untuk papan finit:
Uji di sini dengan bukti terhadap solusi rekursif
sumber