Pemrograman dinamis dalam masalah olimpiade

Hai

Baru-baru ini saya memecahkan masalah dari arsip Timus Online Judge dan menemukan bagian yang disebut tugas pemrograman dinamis . Jenis tugas ini sangat menarik bagi saya, karena seringkali pendekatan ini memastikan kecepatan dan keanggunan solusi. Apa itu pemrograman dinamis?

Pemrograman dinamis adalah pendekatan untuk memecahkan masalah di mana ada pembagian menjadi subtugas yang "lebih sederhana" dibandingkan dengan yang asli. Kata "dinamis" dekat artinya dengan "induktif": diasumsikan bahwa jawabannya dikenal untuk beberapa makna k, dan saya ingin menemukan jawabannya k+1. Dalam matematika, ini disebut transisi induktif, dan merupakan ide utama pemrograman dinamis.

Contoh sederhana


Tugas yang paling mencolok dan indikatif adalah tugas komputasi njumlah urutan Fibonacci. Diketahui bahwa urutan memiliki sifat-sifat berikut:

F0=F1=1, Fn=Fnβˆ’1+Fnβˆ’2.


Ini segera menyiratkan rumus pengulangan:

int Fibonacci(int n) { if(n == 1 || n == 2) return 1; return Fibonacci(n-1) + Fibonacci(n-2); } 

Jika rekursi mencari angka "dari akhir", maka metode berikut secara berurutan menghitung semua angka yang terletak di antara 0dan n:

 int dpFibonacci(int n) { int prev1 = 1; int prev2 = 1; int curr = 0; for(int j = 2; j < n; j++) { curr = prev1 + prev2; prev1 = prev2; prev2 = curr; } return curr; } 

Jelas bahwa untuk yang cukup besar nalgoritma ini bekerja lebih cepat: tidak menghitung nilai antara beberapa kali. Pertimbangkan contoh yang sedikit lebih rumit.

Contoh 1. Anda berjalan di tangga tol. Untuk melangkah ilangkah Anda harus membayar aikoin. Anda dapat melangkah ke langkah berikutnya atau melompati satu langkah. Tugas: untuk lulus nlangkah dan menghabiskan koin sesedikit mungkin.

Jelas bahwa melangkah di setiap langkah, kami meminimalkan jumlah "pembayaran", tetapi kami bisa mengalami langkah yang sangat mahal, yang ingin kami hindari. Buat array nilai ddi mana pada j-tempat akan menjadi (minimum) jumlah koin yang harus dikeluarkan untuk sampai ke jlangkah th. Segera jelas itu d1=a1, d2=a2. Dan kemudian kita akan mengambil minimal dua langkah sebelumnya dan menambahkan biaya langkah itu sendiri:

di= min kiri(diβˆ’1,diβˆ’2 kanan)+ai.



Kami sedikit mengubah kondisi masalah: misalkan pada beberapa langkah Anda bisa mendapatkan koin (ini artinya ak<0) Apa yang perlu diubah dalam algoritma sehingga memberikan hasil yang benar?

Solusi
Kita hanya perlu mengubah "awal" dinamika kita. Jika tangga pertama tidak membawa kita koin, maka disarankan untuk melompatinya, jika a1<0, lebih baik untuk melangkah dan mengumpulkan koin Anda. Jadi d2= mnt kiri(0,d1 kanan)+a2.

Pertimbangkan contoh lain yang menggunakan dinamika "dua dimensi".

Contoh 2. Di labirin ada n kalimkamar, yang masing-masing berisi emas (dalam sangkar (i,j)kebohongan aijemas). Tugasnya adalah menentukan jumlah maksimum emas yang dapat dikumpulkan dengan rute optimal dari suatu titik (0,0)to the point (n,m)jika Anda bisa turun atau ke kanan.

Jadi, kami ingin tahu rute terbaik ke sel (i,j). Kita bisa sampai di sini dari dua sel - (iβˆ’1,j)dan (i,jβˆ’1). Mengingat bahwa rute optimal untuk dua sel ini diketahui (mereka disimpan dalam beberapa tabel d), lalu jawaban untuk sel (i,j)diperoleh sebagai berikut:

dij= max kiri(diβˆ’1j,dijβˆ’1 kanan)+aij.


Ini adalah tugas pemrograman dinamis klasik lainnya, modifikasi yang cukup umum dalam tugas pemrograman olahraga. Tugas serupa dijelaskan secara lebih rinci di sini .

Tugas yang lebih menantang

Jika diinginkan, pendekatan dinamis dapat kacau di mana pun Anda inginkan. Pertimbangkan tugas dari arsip Hakim Timus Online.

Perumusan matematika dari masalah adalah sebagai berikut: diperlukan untuk menemukan jumlah minimum syarat yang diperlukan untuk menguraikan angka yang diberikan ke dalam kotak penuh.

Seperti sebelumnya, anggaplah kita tahu jawaban untuk semua angka kβˆ’1yang disimpan dalam beberapa array ddan kami ingin menemukannya dk.

Ambil nomor ini kdan menganalisis situasi apa yang mungkin terjadi:

  1. kadalah kotak penuh. Dalam hal ini dk=1.
  2. Mungkin nomor sebelumnya kβˆ’1adalah kotak lengkap. Lalu dk=dkβˆ’1+1.

Secara umum, opsi untuk menambahkan unit ke unit sebelumnya tampaknya tidak terlalu buruk.

Kami melanjutkan sebagai berikut: kami mencari dekomposisi k=q2+ssedemikian rupa

dq2+ds<dkβˆ’1+1.

Sejak q2- kotak penuh itu dq2=1, dan

ds<dkβˆ’1,

yaitu, kami menemukan partisi yang lebih baik daripada dkβˆ’1+1, dan jawabannya dalam kasus ini adalah

dk=ds+dq2=ds+1.



Contoh kode Java yang mengimplementasikan algoritma ini:
 for(int k = 1; k <= n; k++) { int best = d[k - 1] + 1; //      int q = 1; while(k - q*q >= 0) { // k = q*q + s if(k - q*q == 0) { // k -   best = 1; break; } else if(d[k - q*q] < best) best = d[k - q*q] + 1; q++; } d[k] = best; } 


Pertimbangkan masalah berikut. Tujuannya adalah untuk membangun tangga dari Nkubus sesuai aturan:

  1. tangga memiliki setidaknya dua langkah;
  2. tangga tidak dapat memiliki dua langkah yang identik;
  3. langkah-langkah tangga berjalan dalam urutan menaik (yaitu, yang berikutnya lebih besar dari yang sebelumnya).

Kali ini kita akan membangun dinamika dua dimensi. Buat tabel ddi mana posisinya (i,j)jumlah tangga yang terdiri dari ikubus yang tingginya tidak melebihi j. Jika berhasil, maka jawaban untuk masalah kita adalah jumlah

 jumlah limitj=1ndnj.


Jadi, kita akan memecahkan masalah menemukan jumlah tangga yang terdiri dari ikubus yang tinggi j. Gambar menunjukkan tangga yang jatuh ke d74:

Karena kita tahu semua tangga, yang terdiri dari lebih sedikit kubus, kita akan β€œmemisahkan” tangga (i,j)kolom kanan. Hasilnya adalah tangga c iβˆ’jkubus. Contoh untuk i=9, j=4:

Tetapi untuk tangga seperti itu, hasilnya sudah diketahui, jadi kami akan memilah-milah semua tangga dengan siklus masuk kdan tambahkan semua hasil. Dengan cara ini

dij= jumlah limitk=1jβˆ’1diβˆ’jk.


Sekarang kita akan mengurutkan ketinggian tangga:

dij= jumlah limitk=1jβˆ’1diβˆ’jk, j= overline1,i.

Akhirnya berubah idari 1sebelumnya nkami mendapatkan jawabannya.

Penting : dalam proses membangun matriks kita, perlu dipertimbangkan dii=1, karena kalau tidak beberapa jenis tangga akan "hilang" (ketika "memisahkan diri"), tetapi tidak perlu dikatakan bahwa tangga seperti itu tidak memenuhi kondisi masalah, jadi jawabannya adalah nomornya. dnnβˆ’1.

Contoh kode Java yang mengimplementasikan algoritma ini:

 dp = new long[n + 1][n+1]; d[1][1] = 1; d[2][1] = 0; d[2][2] = 1; for(int i = 3; i < n + 1; i++) { for(int j = 2; j <i; j++) { long cnt = 0; for(int k = 1; k < j; k++) { cnt += d[i - j][k]; } d[i][j] = cnt; } d[i][i] = 1; //    } long answer = 0L; for(int i = 0; i <= n; i++) { answer += d[n][i]; } answer--; //     


Tugas selanjutnya diselesaikan menggunakan array satu dimensi.

Jadi apa yang kita miliki. Ent pertama tahu 2 kata. Setiap ent mengajarkan semua kata yang dia tahu dirinya sendiri dua sen: muda dan tua. Pada gilirannya, kaum muda diajarkan sebanyak mungkin kata yang dia tahu, dan yang lama hanya diajarkan satu kata. Anda perlu tahu berapa banyak yang tahu persis Kkata-kata (perlu untuk menyimpulkan jumlah modul ini P)

Solusinya cukup sederhana. Buat sebuah array ddi mana pada i-tempat kita akan menyimpan jumlah en (modulo P) siapa tahu ikata-kata. Itu semua dimulai dengan ent pertama, yang tahu dua kata, oleh karena itu d2=1. Dan kemudian semuanya sederhana:

  • Semua kata yang tahu jumlah kata ganjil sudah tua dan hanya bisa dipelajari dari kata-kata sebelumnya. Karena itu aneh i: di=diβˆ’1;
  • Adapun es yang tahu jumlah kata yang genap, ini adalah mereka yang menerima jumlah kata yang sama dari elf (muda) +mereka yang telah belajar dari yang sebelumnya (lama); bahkan untuk ikami punya di=di backslash2+diβˆ’1.

Masih berurusan dengan modulo perhitungan. Agar tidak menyimpan angka besar, kami akan segera mengingat semua nilai modulo.

Contoh kode Java yang mengimplementasikan algoritma ini:
 int[] d = new int[K + 1]; if(K >= 2) d[2] = 1; if(P != 1) { for(int i = 3; i <= K; i++) { if(i % 2 != 0) { d[i] = d[i - 1]; } else { d[i] = ((d[i/2] % P) + d[i - 1] % P) % P; } } } else d[K] = 0; 


Sumber daya yang digunakan:

  1. Hakim Online Timus;
  2. Sedikit tentang pemrograman dinamis;
  3. Modulo properti perbandingan.

Source: https://habr.com/ru/post/id418867/


All Articles