Saat memikirkan satu masalah, saya menyadari bahwa saya perlu membuat algoritma yang efisien untuk menyelesaikan tugas berikut:
Masalahnya: kita diberi kotak persegi dua dimensi dari sisi yang sisi-sisinya sejajar dengan sumbu. Kita bisa melihatnya dari atas. Namun, ada juga segmen horisontal. Setiap segmen memiliki bilangan bulat koordinat ( ) dan koordinat ( ) dan menghubungkan titik dan (lihat gambar di bawah).
Kami ingin tahu, untuk setiap segmen unit di bagian atas kotak, seberapa dalam kita dapat melihat secara vertikal di dalam kotak jika kita melihat melalui segmen ini.
Contoh: diberikan dan segmen yang terletak seperti pada gambar di bawah ini, hasilnya adalah (5, 5, 5, 3, 8, 3, 7, 8, 7) . Lihatlah seberapa dalam cahaya bisa masuk ke dalam kotak.m = 7 ( 5 , 5 , 5 , 3 , 8 , 3 , 7 , 8 , 7 )
Untungnya bagi kita, baik dan adalah cukup kecil dan kita dapat melakukan perhitungan off-line.
Algoritma termudah yang menyelesaikan masalah ini adalah brute-force: untuk setiap segmen melintasi seluruh array dan memperbaruinya jika perlu. Namun, itu memberi kita tidak terlalu mengesankan .
Peningkatan yang bagus adalah dengan menggunakan pohon segmen yang mampu memaksimalkan nilai pada segmen selama kueri dan membaca nilai akhir. Saya tidak akan menjelaskan lebih lanjut, tetapi kita melihat bahwa kompleksitas waktu adalah .
Namun, saya menghasilkan algoritma yang lebih cepat:
Garis besar:
Urutkan segmen dalam urutan menurun koordinat (waktu linier menggunakan variasi jenis penghitungan). Sekarang perhatikan bahwa jika setiap segmen -unit telah dicakup oleh segmen mana pun sebelumnya, tidak ada segmen berikut yang dapat mengikat berkas cahaya melalui segmen -unit ini lagi. Kemudian kita akan melakukan sapuan garis dari atas ke bawah kotak.x x
Sekarang mari kita perkenalkan beberapa definisi: segmen -unit adalah segmen horizontal imajiner pada sapuan yang koordinatnya bilangan bulat dan panjangnya adalah 1. Setiap segmen selama proses sapuan mungkin tidak ditandai (yaitu, berkas cahaya yang berasal dari bagian atas kotak dapat mencapai segmen ini) atau ditandai (huruf besar). Pertimbangkan segmen -unit dengan , selalu tanpa tanda. Mari kita juga memperkenalkan set . Setiap set akan berisi seluruh urutan segmen -unit bertanda yang ditandai (jika ada) dengan tanda berikut yang tidak ditandaix x x 1 = n x 2 = n + 1 S 0 = { 0 } , S 1 = { 1 } , ... , S n = { n } x segmen.
Kami membutuhkan struktur data yang dapat beroperasi pada segmen ini dan menetapkan secara efisien. Kami akan menggunakan struktur find-union yang diperluas oleh bidang yang memegang indeks segmen -unit maksimum (indeks segmen yang tidak ditandai ).
Sekarang kita dapat menangani segmen dengan efisien. Katakanlah kita sekarang mempertimbangkan segmen -th agar (menyebutnya "query"), yang dimulai di dan berakhir di . Kita perlu menemukan semua segmen -unit tak bertanda yang terdapat di dalam segmen ke- (ini persis segmen di mana berkas cahaya akan berakhir dengan caranya). Kami akan melakukan yang berikut: pertama, kami menemukan segmen tanpa tanda pertama di dalam kueri ( Temukan perwakilan dari set yang berisi dan dapatkan indeks maksimum dari set ini, yang merupakan segmen tanpa tanda menurut definisi ). Maka indeks inix 1 x 2 x i x 1 x y x x + 1 x ≥ x 2 ada di dalam kueri, tambahkan ke hasil (hasil untuk segmen ini adalah ) dan tandai indeks ini ( Kumpulan Union berisi dan ). Kemudian ulangi prosedur ini sampai kami menemukan semua segmen yang tidak ditandai , yaitu, selanjutnya Cari kueri memberi kami indeks .
Perhatikan bahwa setiap operasi pencarian-serikat kerja akan dilakukan hanya dalam dua kasus: apakah kita mulai mempertimbangkan segmen (yang dapat terjadi kali) atau kami baru saja menandai segmen unit (ini bisa terjadi kali). Dengan demikian kompleksitas keseluruhan adalah ( adalah fungsi Ackermann terbalik ). Jika ada sesuatu yang tidak jelas, saya bisa menguraikan lebih lanjut tentang ini. Mungkin saya akan dapat menambahkan beberapa gambar jika saya punya waktu.x n O ( ( n + m ) α ( n ) ) α
Sekarang saya mencapai "tembok". Saya tidak dapat menghasilkan algoritma linier, meskipun sepertinya harus ada satu. Jadi, saya punya dua pertanyaan:
- Apakah ada algoritma linear-waktu (yaitu, ) memecahkan masalah visibilitas segmen horizontal?
- Jika tidak, apa buktinya bahwa masalah visibilitas adalah ?
Jawaban:
find ( ) dapat diimplementasikan menggunakan array bit dengan n bit. Sekarang setiap kali kita menghapus atau menambahkan elemen ke L kita dapat memperbarui integer ini dengan menetapkan bit masing-masing menjadi benar atau salah. Sekarang Anda memiliki dua opsi tergantung pada bahasa pemrograman yang digunakan dan asumsi n relatif kecil yaitu lebih kecil dari l o n g l o n g i n t yang setidaknya 64 bit atau jumlah tetap dari bilangan bulat ini:max n L n longlongint
Saya tahu ini adalah hack karena mengasumsikan nilai maksimum untuk dan karenanya n dapat dilihat sebagai konstanta kemudian ...n n
sumber
long long int
sebagai setidaknya tipe integer 64-bit. Namun, bukankah jika besar dan kita menyatakan ukuran kata sebagai w (biasanya w = 64 ), maka masing - masing akan mengambil O ( nfind
waktu? Maka kita akan berakhir dengan totalO(mnSaya tidak memiliki algoritma linier, tetapi yang ini tampaknya O (m log m).
Urutkan segmen berdasarkan koordinat pertama dan tinggi. Ini berarti bahwa (x1, l1) selalu datang sebelumnya (x2, l2) setiap kali x1 <x2. Juga, (x1, l1) pada ketinggian y1 datang sebelum (x1, l2) pada ketinggian y2 setiap kali y1> y2.
Untuk setiap subset dengan koordinat pertama yang sama, kami melakukan hal berikut. Biarkan segmen pertama menjadi (x1, L). Untuk semua segmen lain dalam subset: Jika segmen lebih panjang dari yang pertama, kemudian ubah dari (x1, xt) menjadi (L, xt) dan tambahkan ke subset L-dalam urutan yang tepat. Kalau tidak jatuhkan. Akhirnya, jika subset berikutnya memiliki koordinat pertama kurang dari L, maka bagi (x1, L) menjadi (x1, x2) dan (x2, L). Tambahkan (x2, L) ke subset berikutnya dalam urutan yang benar. Kita bisa melakukan ini karena segmen pertama dalam subset lebih tinggi dan mencakup rentang dari (x1, L). Segmen baru ini mungkin yang mencakup (L, x2), tetapi kita tidak akan tahu itu sampai kita melihat subset yang memiliki koordinat pertama L.
Setelah kami menjalankan semua subset, kami akan memiliki seperangkat segmen yang tidak tumpang tindih. Untuk menentukan nilai Y untuk X yang diberikan, kita hanya perlu menjalankan segmen yang tersisa.
Jadi apa kerumitannya di sini: Jenisnya adalah O (m log m). Looping melalui subset adalah O (m). Pencarian juga O (m).
Jadi sepertinya algoritma ini tidak bergantung pada n.
sumber