Saya menanyakan pertanyaan ini pada StackOverflow , tapi saya pikir di sini adalah tempat yang lebih tepat.
Ini adalah masalah dari kursus Pengantar algoritma :
Anda memiliki array dengan bilangan bulat positif (array tidak perlu diurutkan atau elemen unik). Sarankan algoritma untuk menemukan jumlah elemen terbesar yang dapat dibagi oleh .
Contoh: . Jawabannya adalah 56 (dengan elemen 6 , 13 , 4 , 8 , 25 )
Ini relatif mudah untuk menemukannya di menggunakan pemrograman dinamis dan menyimpan jumlah terbesar dengan sisa .
Juga, jika kita membatasi perhatian pada urutan elemen yang berdekatan, mudah untuk menemukan urutan yang optimal dalam waktu , dengan menyimpan jumlah parsial modulo : misalkan , untuk setiap sisa ingat indeks terbesar sehingga , dan kemudian untuk setiap Anda menganggap manaadalah indeks yang sesuai dengan .
Tetapi apakah ada solusi -waktu untuk kasus umum? Setiap saran akan dihargai! Saya menganggap ini ada hubungannya dengan aljabar linier tapi saya tidak yakin apa tepatnya.
Atau, dapatkah ini dilakukan dalam waktu ?
sumber
Jawaban:
Berikut adalah beberapa ide acak:
Algoritma pemrograman dinamis dapat dibalik untuk mencari jumlah terkecil, bukan jumlah terbesar. Anda akhirnya mencari jumlah kongruen ke sisa jumlah seluruh array, bukan satu kongruen ke nol. Jika kami memproses elemen dalam urutan yang meningkat, ini terkadang memungkinkan algoritma dinamis untuk mengakhiri sebelum memproses seluruh array.
Biaya akan menjadi jika kita memproses elemen . Ada tidak lebih rendah terikat pada algoritma ini karena kita tidak harus menyortir semua elemen. Hanya membutuhkan waktu untuk mendapatkan elemen terkecil .O(nk) k Ω(nlogn) O(nlogk) k
Jika kita peduli dengan himpunan dengan ukuran terbesar, alih-alih himpunan dengan jumlah terbesar, kita mungkin dapat menggunakan perkalian polinomial berbasis transformasi fourier cepat untuk menyelesaikan masalah dalam waktu. Mirip dengan apa yang dilakukan di 3SUM ketika rentang domain terbatas. (Catatan: gunakan kuadrat berulang untuk melakukan pencarian biner, kalau tidak Anda akan mendapatkan mana adalah jumlah elemen yang dihilangkan.)O(n(logn)2(loglogn)) O(nk(logn)(loglogn)) k
Ketika adalah gabungan, dan hampir semua sisa adalah kelipatan dari salah satu faktor , waktu yang signifikan dapat dihemat dengan memfokuskan pada sisanya yang bukan merupakan kelipatan dari faktor itu.n n
Ketika sisa
r
adalah sangat umum, atau hanya ada beberapa sisa yang ada, melacak 'slot terbuka berikutnya jika Anda mulai dari sini dan terus maju denganr
' informasi dapat menghemat banyak pemindaian untuk lompatan-ke-tempat-terbuka waktu.Anda dapat mencukur faktor log dengan hanya melacak jangkauan dan menggunakan topeng bit (dalam algoritma dinamis terbalik), lalu mundur setelah Anda mencapai target yang tersisa.
Algoritma pemrograman dinamis sangat memungkinkan untuk dijalankan secara paralel. Dengan prosesor untuk setiap slot buffer, Anda dapat beralih ke . Sebagai alternatif, dengan menggunakan luas , dan membagi dan menaklukkan agregasi alih-alih agregasi iteratif, biaya kedalaman rangkaian bisa sampai ke .O(n) O(n2) O(log2n)
(Meta) Saya sangat curiga bahwa masalah yang Anda berikan adalah jumlah yang berdekatan . Jika Anda ditautkan dengan masalah aktual, akan mudah untuk memverifikasi itu. Kalau tidak, saya sangat terkejut dengan betapa sulitnya masalah ini, mengingat bahwa itu diberikan dalam kursus yang disebut "Pengantar Algoritma". Tapi mungkin Anda membahas trik di kelas yang membuatnya sepele.
sumber
Algoritme yang saya usulkan berjalan sebagai berikut:
Jumlah dapat dibagi oleh n jika Anda hanya menambahkan jumlah yang merupakan kelipatan dari n.
Sebelum Anda mulai, Anda membuat hashmap dengan kunci int as dan daftar indeks sebagai nilai. Anda juga membuat daftar hasil yang berisi indeks.
Anda kemudian mengulang array dan menambahkan setiap indeks yang mod n adalah nol ke daftar hasil Anda. Untuk setiap indeks lain Anda melakukan hal berikut:
Anda kurangi nilai mod n dari indeks ini dari n. Hasil ini adalah kunci untuk hashmap Anda yang menyimpan indeks untuk elemen dengan nilai yang diperlukan. Sekarang, Anda menambahkan indeks ini ke daftar di hashmap dan melanjutkan.
Setelah Anda selesai mengulangi array Anda menghitung output. Anda melakukan ini dengan mengurutkan setiap daftar dalam hashmap sesuai dengan nilai yang ditunjukkan oleh indeks. Sekarang Anda menganggap setiap pasangan dalam hashmap yang menyimpulkan hingga n. Jadi jika n = 7 Anda mencari hashmap untuk 3 dan 4. Jika Anda mendapat entri di kedua Anda mengambil dua nilai terbesar menghapusnya dari daftar mereka dan menambahkannya ke daftar hasil Anda.
Rekomendasi terakhir: masih tidak menguji algoritma, menulis testcase terhadapnya menggunakan algoritma brute force.
sumber
gunakan metode DP ini dari ( /programming/4487438/maximum-sum-of-non-consecutive-elements?rq=1 ):
Diberikan array A [0..n], misalkan M (i) menjadi solusi optimal menggunakan elemen dengan indeks 0..i. Kemudian M (-1) = 0 (digunakan dalam perulangan), M (0) = A [0], dan M (i) = maks (M (i - 1), M (i - 2) + A [i ]) untuk i = 1, ..., n. M (n) adalah solusi yang kita inginkan. Ini O (n) . Anda dapat menggunakan larik lain untuk menyimpan pilihan mana yang dibuat untuk setiap sub-masalah, dan memulihkan elemen yang sebenarnya dipilih.
Ubah rekursi menjadi M (i) = maks (M (i - 1), M (i - 2) + A [i]) sedemikian sehingga hanya disimpan jika dapat dibagi oleh N
sumber