ამოცანები რომლებიც იხსნება
დინამიკური პროგრამირების
       საშუალებით.
        მარიამ გობრონიძე
ძირითდი თემები
•   Longest Palindromic Substring,
•   Unique Paths,
•   Unique Paths II,
•   Longest Common Subsequence
Longest Palindromic Substring
ვთქვათ მოცემულია სტრიქონი s. უნდა ვიპოვოთ ამ სტრიქონის
ქვესტრიქონებიდან ყველაზე გრძელი პალინდრომი. თანაბარი სიგრძის
შემთხვევაში, უნდა დაბრუნდეს ის ქვესტრიქონი, რომელიც პირველად
გვხვდება სტრიქონში.
მაგალითები
Generating all sub-strings - O(n^3) time
and O(1) space
Using Dynamic Programming TD- O(n^2)
time and O(n^2) space
Using Dynamic Programming BU- O(n^2)
time and O(n^2) space
იდეა
გამოვიყენოთ დინამიკური პროგრამირება (Dynamic
Programming) — შევინახოთ მცირე ქვესტრიქონების
პალინდრომული სტატუსი და ამ ინფორმაციის გამოყენებით
გადავწყვიტოთ უფრო გრძელი ქვესტრიქონი არის თუ არა
პალინდრომი.
იდეა
თუ ვიცით, რომ სტრიქონის [i, j] მონაკვეთი პალინდრომია, მაშინ
შეგვიძლია მარტივად შევამოწმოთ არის თუ არა [i-1, j+1] მონაკვეთი
პალინდრომი — ამისათვის საკმარისია მხოლოდ შევადაროთ
სიმბოლოები str[i-1] და str[j+1].
იდეა
თუ [i, j] ქვესტრიქონი არ არის პალინდრომი, მაშინ [i-1, j+1] მით უმეტეს
ვერ იქნება პალინდრომი. ხოლო თუ [i, j] არის პალინდრომი, მაშინ [i-1,
j+1] იქნება პალინდრომი მხოლოდ იმ შემთხვევაში, თუ str[i-1] ==
str[j+1].
იდეა
ამ პრინციპზე დაყრდნობით შეგვიძლია შევქმნათ ორგანზომილებიანი მასივი
table[][], რომელიც ინახავს ინფორმაციას იმის შესახებ არის თუ არა
str[i..j] ქვესტრიქონი პალინდრომი.
შემდეგ ვამოწმებთ ყველა ქვესტრიქონს სიგრძით 1-დან N-მდე და ვარკვევთ მათ
“პალინდრომულობას” ზემოთქმული წესის მიხედვით. საბოლოოდ, ვპოულობთ იმ
ქვესტრიქონს, რომელიც ყველაზე გრძელია და არის პალინდრომი — ეს იქნება
პასუხი.
Unique Paths

მოცემულია m x n ზომის ბადე.
 რობოტი თავდაპირველად მდებარეობს ბადის ზედა მარცხენა კუთხეში
(ანუ grid[0][0]).
 მისი მიზანია, მიაღწიოს ბადის ქვედა მარჯვენა კუთხეს (ანუ grid[m -
1][n - 1]).
რობოტს შეუძლია გადაადგილება მხოლოდ ქვემოთ ან მარჯვნივ
ნებისმიერ დროს.

მოცემულია ორი მთელი რიცხვი m და n. უნდა დააბრუნოთ ისეთი
განსხვავებული გზების რაოდენობა, რომელთა გავლითაც რობოტს
შეუძლია მიაღწიოს ქვედა მარჯვენა კუთხემდე.
Unique Paths II
მოცემულია მთელი რიცხვების მატრიცა grid, ზომით m x n.

რობოტი თავდაპირველად მდებარეობს ბადის ზედა მარცხენა კუთხეში (ანუ
grid[0][0]) და მისი მიზანია მიაღწიოს ქვედა მარჯვენა კუთხეს (ანუ
grid[m - 1][n - 1]).
რობოტს შეუძლია გადაადგილება მხოლოდ ქვემოთ ან მარჯვნივ ნებისმიერ
დროს.

ბადეში:

 ● 0 აღნიშნავს თავისუფალ უჯრედს (რომელზეც გადაადგილება
   შეიძლება),
 ● 1 აღნიშნავს ბარიერს (რომელზეც გადაადგილება აკრძალულია).
გზა, რომელსაც რობოტი გადის, არ უნდა გადიოდეს ბარიერზე.

დააბრუნეთ ისეთი უნიკალური გზების რაოდენობა, რომლითაც რობოტს შეუძლია
ქვედა მარჯვენა კუთხემდე მიღწევა ბარიერების გვერდის ავლით.
Longest Common Subsequence

მოცემულია ორი სტრიქონი — s1 და s2.
ამოცანაა, ვიპოვოთ მათი საერთო ქვესტრინგის (Longest Common
Subsequence - LCS) მაქსიმალური სიგრძე.
თუ საერთო ქვესტრინგი არ არსებობს, დააბრუნეთ 0.
ქვესტრინგი (subsequence) — სტრიქონია, რომელიც მიღებულია საწყისი სტრიქონიდან
0 ან მეტი სიმბოლოს ამოჭრით, ისე, რომ დარჩენილი სიმბოლოების თანმიმდევრობა არ
იცვლება.

მაგალითად, სტრიქონის "ABC" ქვესტიქონებია:
 "" (ცარიელი), "A", "B", "C", "AB", "AC", "BC", "ABC".

ზოგადად, სიგრძით n სტრიქონს გააჩნია 2ⁿ ქვესტრიქონი.
მაგალითები
Using Recursion - O(2 ^ min(m, n)) Time
and O(min(m, n)) Space
იდეა

ვადარებთ სტრიქონების s1 და s2 ბოლო სიმბოლოებს. შედარებისას ორი
შემთხვევა არსებობს:

დამთხვევა (Match):
თუ ბოლო სიმბოლოები ემთხვევა, ვიძახებთ რეკურსიულად ფუნქციას
დარჩენილი სტრიქონებისთვის (ანუ სიგრძის m-1 და n-1 სტრიქონებისთვის) და
ვამატებთ 1-ს შედეგს.
იდეა

არ ემთხვევა (Do not Match):
გვჭირდება ორი რეკურსიული გამოძახება:

     ○   ერთი სტრიქონის s1 შემცირებული ვერსიით (m-1, n);

     ○   მეორე s2-ის შემცირებული ვერსიით (m, n-1);
          საბოლოოდ, ვიბრუნებთ ამ ორიდან მაქსიმალურ მნიშვნელობას.
საბაზისო შემთხვევა (Base case):
თუ რომელიმე სტრიქონი ცარიელია (ანუ სიგრძე არის 0), მაშინ ვაბრუნებთ 0-
ს.
Using Memoization (Top Down DP) - O(m *
n) Time and O(m * n) Space
Using Bottom-Up DP (Tabulation) - O(m *
n) Time and O(m * n) Space
რეკურსიული გადაწყვეტის ოპტიმიზაციისთვის ვიყენებთ
ორგანზომილებიან მემორიზაციის ცხრილს ზომით (m + 1) × (n + 1).

ამ ცხრილში ვინახავთ უკვე გამოთვლილ მნიშვნელობებს, რათა
ავიცილოთ იგივე ქვემოამოცანების განმეორებითი გამოთვლა.

რეკურსიის გაშვებამდე თითოეულ შემთხვევაში ვამოწმებთ ამ ცხრილს —
თუ მნიშვნელობა უკვე დათვლილია, მას პირდაპირ ვიყენებთ.

ეს მიდგომა (მემორიზაცია ან ტაბულაცია) მნიშვნელოვნად ამცირებს
გამოთვლების რაოდენობას და ზრდის ალგორითმის ეფექტიანობას.
რეკურსიულ გადაწყვეტაში ორი პარამეტრია, რომლებიც ცვალებადია —
ეს პარამეტრები მერყეობს დიაპაზონში 0-დან m-ამდე და 0-დან n-ამდე.

ამიტომ ვქმნით ორგამზომილებიან dp მასივს ზომით (m + 1) × (n +
1).

პირველ რიგში ვავსებთ იმ უჯრებს, სადაც ერთ-ერთი პარამეტრი — m ან
n — არის 0, რადგან ამ შემთხვევებში შედეგი ცნობილია.

შემდეგ ეტაპობრივად ვავსებთ დანარჩენ უჯრებს რეკურსიული
ფორმულის საფუძველზე, რაც გულისხმობს წინა შედეგებზე
დაყრდნობით ახალი მნიშვნელობების დათვლას.
გმადლობთ ყურადღებისთვის!
