W Bloku 03 zaimplementowaliśmy rekurencyjną funkcję Fibonacciego o złożoności O(2ⁿ) — ponieważ te same podproblemy (np. fib(30)) są liczone wielokrotnie. Programowanie dynamiczne (PD) rozwiązuje ten problem, zapamiętując (memoizując) już obliczone wyniki.
Klasyczny problem PD: dla dwóch ciągów znaków znajdź najdłuższy podciąg (niekoniecznie spójny) występujący w obu. Rozwiązanie buduje tablicę dwówymiarową dp, gdzie dp[i][j] to długość LCS dla pierwszych i znaków pierwszego i pierwszych j znaków drugiego ciągu.
Algorytm zachłanny podejmuje w każdym kroku decyzję lokalnie najlepszą, bez cofania się — i licząc na to, że suma lokalnie optymalnych wyborów da rozwiązanie globalnie optymalne. Działa to tylko dla pewnych problemów!
Dla nominałów [1, 3, 4] i kwoty 6, algorytm zachłanny wyda 4+1+1 (3 monety), podczas gdy optymalnie można 3+3 (2 monety)! Zachłanność działa poprawnie tylko dla pewnych zestawów nominałów (m.in. polskiego systemu monet) — ogólnie problem wydawania reszty wymaga PD.
Algorytm Kadane'a znajduje maksymalną sumę spójnego (ciągłego) podciągu w czasie liniowym O(n):
Zaprojektuj (na kartce lub w kodzie) algorytm PD znajdujący długość najdłuższego niemalejącego podciągu w tablicy liczb, np. dla [3, 1, 4, 1, 5, 9, 2, 6] wynik to 4 (podciąg 1,4,5,9 lub 1,1,5,9).
Zaimplementuj rozwiązanie problemu wydawania reszty metodą PD (przeszukując wszystkie możliwe nominały dla każdej kwoty od 0 do docelowej) i porównaj wynik z wersją zachłanną dla nominałów [1, 3, 4] i kwoty 6.
1. Jak programowanie dynamiczne przyspiesza obliczanie ciągu Fibonacciego?
2. Czy algorytm zachłanny zawsze daje optymalne rozwiązanie problemu wydawania reszty?
3. Jaką złożoność czasową ma algorytm Kadane'a znajdujący maksymalną sumę spójnego podciągu?