You are on page 1of 61

Elektrotehnički fakultet Univerziteta u Beogradu

Odsek za softversko inženjerstvo


Praktikum iz programiranja 2

Dinamičko
programiranje

Marko Mišić
marko.misic@etf.bg.ac.rs
Vladislav Guberinić
neosisani@gmail.com
Aleksandar Ivanović
aleksandar.ivanovic.94@gmail.com
Uvod u dinamičko programiranje (1)
Dinamičko programiranje je
metod rešavanja kompleksnih problema
koji se mogu razbiti
na potprobleme manje složenosti
Koristi se najčešće kod problema optimizacije
Problemi moraju imati
optimalnu substrukturu
Pritom, potproblemi se preklapaju
Za razliku od problema koji se rešavaju
npr. divide and conquer algoritmima
Uvod u dinamičko programiranje (2)
Rešenje jednog potproblema se
može iskoristiti u rešavanju drugog,
složenijeg potproblema
Svaki potproblem se tipično rešava samo jednom
Koristi se tehnika
memo(r)izacije za skladištenje rešenja potproblema
Nakon što je rešenja potproblema izračunato,
svaki sledeći put se koristi
izračunata vrednost prostim pristupom tabeli
Tzv. „tablični“ metod
Posebno efikasno ukoliko
broj ponavljenih potproblema raste
eksponencijalno sa brojem ulaznih podataka
Osobine DP problema
Optimalna podstruktura problema znači
da se rešenje problema može dobiti
kombinovanjem optimalnih rešenja
potproblema
Optimalna podstruktura se
obično opisuje rekurzivno
Identifikacija rekurzije je često
prvi korak u rešavanju problema
Preklapanje potproblema podrazumeva
da je prostor (broj) potproblema relativno mali
Algoritam tipično ne generiše nove potprobleme,
već se stari rešavaju iznova i iznova
Koraci u rešavanju problema
Rešavanje problema korišćenjem
dinamičkog programiranja se obično može podeliti
na sledeća četiri koraka:
Karakterizacija strukture optimalnog rešenja
Rekurzivno definisanje rešenja optimalnog rešenja
Računanje vrednosti optimalnog rešenja
top-down ili bottom-up pristupom
Konstruisanje optimalnog rešenja
na osnovu sračunatih informacija
Koraci 1-3 su osnova dinamičkog programiranja,
dok se korak 4 može izostaviti ukoliko se traži
samo određena vrednost koja karakteriše rešenje,
ne i samo rešenje
Npr. dužina najduže zajedničke podsekvence i sl.
Pristupi u rešavanju problema
Top-down pristup (odozgo na dole)
Problem se rastavlja na potprobleme
Potproblemi se reše i pamte se njihova rešenja
za kasniju upotrebe
Ovaj pristup predstavlja kombinovanje
rekurzije i memoizacije
Bottom-up pristup (odozdo na gore)
Svi potproblemi se redom rešavaju i
koriste za nalaženje većih
Ovaj pristup je bolji zbog štednje
memorijskog prostora
Ponekad teško odrediti
koji su sve potproblemi potrebni za traženje datog
Potrebno je odrediti tzv. graf zavisnosti
Primena dinamičkog programiranja
Izračunavanje Fibonačijevih brojeva
“Matrični” problemi
Pronalaženje najduže
zajedničke podsekvence
Longest common subsequence
Longest increasing subsequence
Prebrojavanje novca
Problem ranca
Fibonačijevi brojevi (1)
Naivna implementacija:
function fib(n)
if n = 0 or n = 1
return n
else
return fib(n − 1) + fib(n − 2)
Događa se kombinatorna eksplozija
fib(5)
fib(4) + fib(3)
(fib(3) + fib(2)) + (fib(2) + fib(1))
((fib(2) + fib(1)) + (fib(1) + fib(0))) + ((fib(1) + fib(0)) +
fib(1))
(((fib(1) + fib(0)) + fib(1)) + (fib(1) + fib(0))) + ((fib(1) +
fib(0)) + fib(1))
Fibonačijevi brojevi (2)
Pristup odozgo na dole koristi memoizaciju
Potproblemi se rešavaju tačno jednom,
a njihove vrednosti se pamte
var m := array[0..n]
m[0] := 1; m[1] := 1;
m[2 : n ] := SENTINEL; {npr. -1}
function fib(n)
if n not in m
m[n] := fib(n − 1) + fib(n − 2)
return m[n]
Fibonačijevi brojevi (3)
Pristupom odozdo na gore, linearna prostorna složenost
se može preobraziti u konstantnu
function fib(n)
var previousFib := 1, currentFib := 1
repeat n − 1 times
var newFib := previousFib + currentFib
previousFib := currentFib
currentFib := newFib
return currentFib
Alternativno:
a := 1; b := 1; c := 0;
for i := 3 to n do
begin
c := a + b;
a := b;
b := c;
end;
Fibonačijevi brojevi –
dodatne optimizacije (1)
Kod za pristup odozdo nagore se može
još dodatno optimizovati tehnikom
razmotavanja (uklanjanja) petlji
(loop unroll)
Štedi se polovina skokova
a := 1; b := 1; c := 0;
for i := 3 to n step 2 do
begin
c := a + b; a := b; b := c;
c := a + b; a := b; b := c;
end;
Fibonačijevi brojevi –
dodatne optimizacije (2)
Konačno, mogu se uštedeti memorijski
transferi izbacivanjem promenljive c:
a := 1; b := 1;
for i := 3 to n step 2 do
begin
a := a + b;
b := a + b;
end;
Svi međurezultati koji se čuvaju
sada staju u registre procesora,
uklonjen je deo skokova i
memorijskih transfera
Fibonačijevi brojevi –
dodatne optimizacije (3)
Ovakve optimizacije uvek smanjuju
čitljivost koda i treba ih retko koristiti!
Mana optimizacije - gde je rezultat?
if (odd(n))
then result := a
else result := b
Maksimalna suma
nesusednih elemenata u nizu (1)
Postavka problema
Dat je niz a prirodnih brojeva dužine n. Odrediti
podniz datog niza čiji je zbir elemenata
maksimalan, a u kome nema susednih elemenata.
Kako definisati potproblem?
Nalaženje traženog podniza
na nekom delu polaznog niza a
Definišemo dodatni niz d[k] koji će u svakom
trenutku čuvati maksimalan zbir nesusednih
elemenata niza (a1,a2,...,ak), za k ∈[1,...,n]
Konačno rešenje će biti smešteno u d[n]
Maksimalna suma
nesusednih elemenata u nizu (2)
Ukoliko imamo sračunat niz d
za prvih k-1 elemenata,
tada element ak možemo dodati u sumu
ukoliko je povećava ili
uzeti vrednost d[k−1]
kao do tada najveću sračunatu sumu
d[k] = max{d[k−1], ak +d[k−2]}, za k ≥3
Početni uslovi će pritom biti:
d[1] = max{0, a1}
d[2] = max{0, a1, a2}
Maksimalna suma
nesusednih elemenata u nizu (3)
Računanje niza d:
d[1] = max{0,a1};
d[1] = max{0,a1,a2};
for k ←3 to n do
if d[k−1] > d[k−2]+a[k] then
d[k] = d[k−1];
else9
d[k] = d[k−2]+a[k];
end
Maksimalna suma
nesusednih elemenata u nizu (4)
Rekonstrukcija podsekvence (u obrnutom poretku)
if n = 1 then
return subsequence = a;
end
subsequence =∅; ∅
currentIndex = n;
while (currentIndex > 0) do
if d[currentIndex] = d[currentIndex−1] then
add a[currentIndex] to subsequence;
currentIndex = currentIndex−2;
else
currentIndex = currentIndex−1;
end
End
return subsequence;
Problem penjanja uz veštačku stenu (1)
Zamislite da treba da se uspnete uz veštačku stenu u
nekom zabavnom parku. Na steni se nalaze ručke za
penjanje koje su raspoređene u kvadratnu mrežu (grid).
Zbog nagiba stene, penjanje uz neke ručke je znatno
opasnije nego uz druge. Sa svake ručke, penjač može da
dohvati i pređe na neku od tri ručke iznad njega – ručku
iznad, ručku iznad desno i ručku iznad levo (osim ukoliko
jedna od ručki levo ili desno nije dostupna, jer je penjač
došao do kraja zida).
Potrebno je pronaći najmanje opasan put od podnožja do
vrha stene. Smatrati da je svakoj ručki dodeljen ceo broj
koji označava težinu penjanja uz njih, a ukupna težina za
neki put će biti jednak sumi težina svih ručki koje su
korišćene na putu do vrha.
Problem penjanja uz veštačku stenu (2)
Problem se može modelirati
matricom dimenzije nxm
Vrednost svakoj polja matrice (i,j) će biti
jednaka težini (ceni) prelaska
preko date ručke (polja matrice)
Sa polja (i,j) se u jednom koraku može preći
na neko od polja:
(i+1,j−1), (i+1,j) ili (i+1,j+1),
za 1 < j < m
(i+1,j), (i+1,j+1)
za j = 1
(i+1,j−1), (i+1,j)
za j = m
Problem penjanja uz veštačku stenu (3)
Primer jedne ulazne matrice (stene)
Minimalna cena penjanja najlakšom putnjom je
12 i počinje od polja (1,4)
Pohlepni pristup bi krenuo od polja (1,2) i
uzimao lokalno najbolje rešenje u svakom
koraku što bi proizvelo put sa cenom 13
1 2 3 4 5
4 2 8 9 5 8
3 4 4 6 2 3
2 5 7 5 6 1
1 3 2 5 4 8
Dno stene
Problem penjanja uz veštačku stenu (4)
Očigledno, problem je rekurzivan,
pa je potrebno definisati strukturu podataka
u kojoj će se čuvati međurezultati
Potrebno je definisati matricu A(i,j) dimenzija nxm
Svaki element matrice A(i,j) će sadržati
cenu (težinu) najmanje opasnog puta
od dna matrice do polja A(i,j) uključujući i to polje

C (i, j ) + min{ A(i − 1, j − 1), A(i − 1, j )}, za j = m



A(i, j ) = C (i, j ) + min{ A(i − 1, j ), A(i − 1, j + 1)}, za j = 1
C (i, j ) + min{ A(i − 1, j − 1), A(i − 1, j ), A(i − 1, j + 1)}, za 1 < j < m

Problem penjanja uz veštačku stenu (5)
Matrica A(i,j) se može inicijalizovati
na dva načina:
A(1,j) = C(1,j) za 1 ≤ j ≤ m
Elegantnije rešenje je da uvedemo dodatnu
nultu vrstu i postavimo A(0,j) = 0 za 1 ≤ j ≤ m
Specijalne slučajeve za računanje A(i,j)
možemo eliminisati uvođenjem
dve dodatne kolone 0 i m+1 i
postavljanjem na neku veoma veliku vrednost
A(i,j) = C(i,j) + min{A(i-1,j-1), A(i-1,j), A(i-1,j+1)}
Problem penjanja uz veštačku stenu (6)
Da bi našli cenu najmanje opasnog puta,
treba da nadjemo minimalnu vrednost
u poslednjoj vrsti matrice:
min1≤j≤m A(n,j)
Primer matrice A za prikazani ulazni primer
∞ 0 0 0 0 0 ∞
∞ 3 2 5 4 8 ∞
∞ 7 9 7 10 5 ∞
∞ 11 11 13 7 8 ∞
∞ 13 19 16 12 15 ∞
Problem penjanja uz veštačku stenu (7)
Inicijalizacija:
for j = 1 to m do
A(0,j) ← 0
for i = 0 to n do
A(i,0) ← INF
A(i,m + 1) ← INF
Računanje:
for i = 1 to n do
for j = 1 to m do
A(i,j) ← C(i,j) + min{A(i−1,j −1), A(i−1,j), A(i−1,j + 1)}
Traženje minimuma:
cost ← INF
for j = 1 to m do
if (A(n,j) < cost) then
cost ← A(n,j)
return cost
Problem penjanja uz veštačku stenu (7)
Rekonstrukcija puta od dna do vrha:
procedure PrintOpt(i,j)
if (i = 0) then return
else if (A(i,j) = C(i,j) + A(i−1,j −1)) then
PrintOpt(i-1,j-1)
else if (A(i,j) = C(i,j) + A(i−1,j)) then
PrintOpt(i-1,j)
else if (A(i,j) = C(i,j) + A(i−1,j + 1)) then
PrintOpt(i-1,j+1)
end_if
print “Cell “ (i,j)
end_PrintOpt
Binomni koeficijenti
Koristi se u raznim verovatnoćama
Često su potrebne vrednosti za svako k
n n!
C =   =
k

 k  (n − k )!k!
n

Rešenje:
popuni matricu jedinicama
for i := 1 to n
for j := 1 to i
pas[i,j] = pas[i-1,j] + pas[i-1,j-1];
Ranac - postavka
Mali Andrija je dobio jednodnevnu kartu do
Beograda da bi posetio Narodnu biblioteku
Srbije. U biblioteci postoji kolekcija od n
naučnih radova, svaki sa po s[i] stranica i
naučnom vrednošću v[i]. Andrija ima svoj
ranac u koji staje najviše S strana i želi da
iznese neke radove iz bibloteke. Kako je
Andrija Piroćanac on želi da vrednost radova
koje će izneti bude što veća. Postoji
beskonačno mnogo kopija svakog rada.
Pomozite Andriji da odabere najvrednije
radove.
Ranac - analiza (1)
Gramzivo rešenje:
Naći rad koji ima najvecu vrednost po strani
Trpati u ranac dok ima mesta
Dopuniti na sličan način do kraja
Problem:
S = 18, n = 2
s[0] = 10, v[0] = 100 (10 $ / strani)
s[1] = 9, v[1] = 81 (9 $ / strani)
Gramzivo rešenje: Ubaciti rad br. 0
Optimalno rešenje: Ubaiciti 2 rada br. 1
Ranac - analiza (2)
Iscrpljivanje grubom silom (brute force):
Isprobamo sve moguće kombinacije
Svaka stvar može da se nađe najviše k puta
k = S / min(s[i])
O(kn) -> eksponencijalno!!!
Ranac - analiza (3)
Dinamičko rešenje
Oktrijemo optimalni način za ranac veličine 1
Na osnovu njega napravimo optimalno rešenje
za ranac veličine 2
Na osnovu njega napravimo optimalno rešenje
za ranac veličine 3
???
Profit
Ranac - pseudokod (1)
Potrebne strukture podataka:
m - veličina ranca
n - broj radova
s[i] - broj strana i-tog rada
v[i] - vrednost i-tog rada
ranac[i] - maksimalna vrednost ranca
veličine i
Ranac - pseudokod (2)
#Inicijalizujemo niz
for i := 1 to n
if (ranac[s[i]] < v[i]) then
ranac[s[i]] := v[i];
#Gradimo iterativno rešenje
for i := 1 to m
for j := 1 to n
if (ranac[i] > (ranac[i-s[j]] + v[j])) then
ranac[i] := ranac[i-s[j]] + v[j])
Ranac - varijacija - sadržaj ranca (1)
Potrebno je znati i sadržaj optimalnog
ranca, ne samo njegovu vrednost.

pret[i] - na koje od prethodnih rešenja smo


se nadogradili
Ranac - varijacija - sadržaj ranca (2)
Modifikacija prethodnog rešenja:
for i := 1 to n
ranac[s[i]] := max(ranac[s[i]], v[i]);
pret[i] := -1;

for i := 1 to m
for j := 1 to n
if (ranac[i] > (ranac[i-s[j]] + v[j])) then
ranac[i] := ranac[i-s[j]] + v[j])
pret[i] := j;
Ranac - varijacija - sadržaj ranca (3)
Ispis sadržaja – rekurzija:
procedure ispisi (i : integer)
begin
if (i = -1) then
return;
else
ispisi ( i - pret[i]);
print pret[i];
end;
Ranac - varijacija - konačan br radova (1)
Postoji konačan broj kopija svakog rada
Pamtimo u svakom trenutku
stanje svih radova

br[t, i] – broj preostalih kopija rada br. i u


trenutku t
Ranac - varijacija - konačan br radova (2)
Rešenje kada je broj kopija konačan:
for i := 1 to n do
if (ranac[s[i]] > v[i]) then
br[i] := br[i-s[j]];
dec(br[i-s[j], j]);
ranac[s[i]] := max(ranac[s[i]], v[i]);
end if
pret[i] := -1;
end for
Ranac - varijacija - konačan br radova (3)
for i := 1 to m
for j := 1 to n
if (ranac[i] > (ranac[i-s[j]] + v[j]))
and (br[i-s[j], j]) then
ranac[i] := ranac[i-s[j]] + v[j])
br[i] := br[i-s[j]];
dec(br[i-s[j], j]);
pret[i] := j;
Bogataš - postavka
Mali Andrija je brže bolje prodao sve
naučne radove što je pokupio u prošlom
zadatku i rešio da svoje novostečeno
bogatstvo potroši na zidanje kuće. Avaj, u
njegovom rodnom Pirotu je donesen zakon
koji zabranjuje seču drveća. Kako su kazne
za seču ogromne, Andrija vas pita kolika je
najveća kuća koju on može da sazida bez
da obori ijedno drvo. Zidovi kuće moraju
biti ili sever-jug ili istok-zapad.
Bogataš - analiza (1)
Teren možemo predstaviti kao kvadratnu
matricu.
Na svakom polju gde se nalazi drvo
upisaćemo -1. Na ostala polja upisujemo 0.
Najveća kuća onda prestavlja najveću
podmatricu sačinjenu isključivo od nula.
Bogataš - analiza (2)
Trivijalno rešenje proverava svaki kvadrat.
Prvo odabira gornju levu, zatim donju
desnu tačku i proverava da li u njima
postoji drvo
Postoji n2 početnih i n2 kranjih tačaka
Obilazak kvadrata je složenosti O(n2)
Ukupna složenost je O(n6)
Bogataš - pravougaona kuća - analiza
Definišemo matricu mat
tako da je mat[i,j] broj polja
ulevo do prvog drveta - O(n2)
Tražimo optimalne kuće
po kolonama - O(n)
U svakoj koloni naći podniz takav da je
min(podniz) * len(podniz) maksimalno
Ovo je zavino od algoritma
O(n2), O(n log n) ili O(n)
Celokupna složenost je O(n3), O(n2 log n) ili O(n2)
Bogataš - pravougaona kuća - kod (1)
#Prvi korak - O(n2)
for i := 1 to n do
for j := 1 to n do
if (mat[i, j] <> -1)
mat[i,j] = mat[i-1, j] + 1;
Bogataš - pravougaona kuća - kod (2)
#Drugi korak – O(n3)
lmax := 0;
for i := 1 to n do
for j := 1 to n do
lmin := mat[i, 1];
for k := j to n do
lmin := min(lmin, mat[i, k])
if ((lmin * (k – j + 1)) > lmax)
lmax := lmin * (k – j + 1);
Bogataš - pravougaona kuća - zaključak
O(n log n)
koristi binarno pretraživanje umesto linearnog
Potrebno stablo => ASP
O (n)
Prave se dva pomoćna niza: levi[i] i desni[i]. Za
svako i je levi[i] indeks prvog levog elementa
većeg od niz[i]. Analogno za desni[i].
Za svako i je površina pravougaonika:
niz[i] * (desni[i] - levi[i] + 1)
Bogataš - kvadratna kuća - analiza
Možemo da uočimo
da za fiksiranu početnu tačku i
dve dužine zida z1 i z2,
ako z1 ima drvo onda će i z2 imati drvo
Ako modifikujemo algoritam
da ovo uzme u obzir tako
da kada prvi put naiđe na drvo pređe
na sledeću početnu taku,
onda mu složenost opada na O(n4)
Bogataš - kvadratna kuća - pseudokod
Rešenje ukoliko je kuća kvadratne osnove:
n - dimenzija placa
mat[n][n] - matrica sa drvećem

for i := 1 to n do
for j := 1 to n do
if (mat[i][j] <> -1) then
m = max (mat[i-1, j-1], mat[i-1, j],
mat[i, j-1]) + 1;
Sumarum (1)
Đurica je pronašao N karata poređanih u niz. Na kartama su
zapisani celi brojevi.
Đurica datom nizu karata A, dodeljuje vrednost f(A) koja je
jednaka sumi razlika vrednosti na uzastopnim kartama.
Đurica želi da izbaci najviše K karata, tako da ne naruši
prvobitan poredak karata u nizu u cilju da maksimizuje
vrednost niza f(A).
Za zadato N i K, kao i početan niz karata A pomozite Đurici da
pronađe maksimalnu vrednost f(A) koju može postići.
Ograničenja:
2 ≤ N ≤ 500.000, 0 ≤ K ≤ N – 2
-1.000.000.000 ≤ A[i] <= 1.000.000.000
Sumarum (2)
Analizirajmo funkciju f(A):
Neka je A =[a1,a2,…an]
f(A) = a2-a1+a3-a2+…+an-an-1 = an-a1
Funkcija f(A) je u stvari jednaka razlici
poslednjeg i prvog elementa niza A
Iz ove činjenice sledi ključna opservacija
potrebna za rešavanje ovog problema:
Za dati niz A brisanje elementa koji nije ni prvi
ni poslednji ne utiče na vrednost f(A)
Sumarum (3)
Na osnovu toga možemo zaključiti
da iz prvobitnog niza treba brisati
samo nekoliko prvih i/ili
nekoliko poslednjih elemenata
Formalno rečeno,
na vrednost f(A) možemo uticati
tako što ćemo obrisati L elemenata
sa početka niza i R elemenata sa kraja niza
tako da važi 0 ≤ L, R i L + R ≤ K
Sumarum (4)
Vrednosti L i R možemo fiksirati
pomoću dve petlje i
pronaći optimalno rešenje
Složenost ovog pristupa je kvadratna u odnosu na K,
što je u ovom problemu previše sporo
Bolje rešenje možemo postići
tako što ćemo malo promeniti
način razmišljanja:
Umesto da fiksiramo L i R koji predstavljaju koliko tačno
brojeva treba obrisati sa početka, odnosno sa kraja niza,
fiksiraćemo L i R koji predstavljaju koliko najviše brojeva
treba obrisati sa početka, odnosno, sa kraja niza
Sumarum (5)
Primetimo da je za fiksirano L, R
jednoznačno određeno:
R=K–L
Sada ostaje samo
da za fiksirano L i izračunato R pronađemo
koji element treba ostati na početku niza,
a koji element treba ostati na kraju niza i
rešili smo zadatak
Sumarum (6)
Možemo primetiti da bi smo dobili
optimalno rešenje prvi element niza
mora biti što manji, a poslednji što veći
Dakle za dato L i R rešenje je:
maxR[R] – minL[L]
gde minL[i] predstavlja minimum
prvih i elemenata sa početka niza,
a maxR[i] maksimum prvih i elemenata
sa kraja niza
Ostaje samo da izračunamo
nizove maxL i maxR
Sumarum (7)
Nizove minL i maxR možemo računati dinamički:
minL[0] ← A[1]
maxR[0] ← A[N]; // početne vrednosti
for i =1 to N do
minL[i] ← min ( A[i], minL[i – 1] );
maxR[i] ← max ( A[N + 1 – i], maxR[i – 1] );
Minimum prvih i elemenata je minimum i-tog
elementa i minimuma prvih i – 1 elemenata,
analogno se popunjava niz maksimuma
Sumarum (8)
Kada imamo izračunate nizove minL i maxR
algoritam za rešavanje ovog problema
izgleda ovako:

Sol ← A[N] – A[1]


for L =0 to K do
R = K – L;
Sol ← max ( Sol, maxR[R] – minL[L] )
return Sol

Složenost je linearna u odnosu na N


Tajno pismo (1)
Poznati privatni detektiv Šerlok Holms je analizirao jednu
šifrovanu poruku. Tokom analize te poruke on je smislio
jedan veoma interesantan problem. Njega je zanimalo
koliko različitih poruka može dobiti ako na datu poruku
primeni proizvoljan broj sledećih operacija:
Neka je data šifrovana poruka S koja se sastoji samo od slova
engleskog alfabeta i sadrži 1≤|S| ≤100 karaktera
Prva operacija karakter na poziciji 1≤i<|S| zameni njegovim
sledbenikom, a karakter na poziciji i+1 njegovim prethodnikom
Druga operacija karakter na poziciji 1≤i<|S| zameni njegovim
prethodnikom, a karakter na poziciji i+1 njegovim sledbenikom
NAPOMENA: ‘a’ nema prethodnika, ‘z’ nema sledbenika tako da
su operacije koje koriste prethodnika ‘a’ i sledbenika ‘z’
nekorektne
Kako rešenje može biti ogromno, Šerloka zanima koliki je
njegov ostatak pri deljenju sa njegovim omiljenim
brojem 1.000.000.007?
Tajno pismo (2)
Ključna stvar u ovom problemu jeste to
da primenom bilo koje operacije mi ne menjamo
“sumu karaktera u poruci”,
odnosno sumu ASCII vrednosti karaktera
koji čine poruku
Ekvivalentan problem ovom problemu jeste
da za datu dužinu reči N i datu sumu karaktera SUM
pronađemo koliko postoji različitih reči dužine N
čija je suma karaktera SUM,
a taj problem se lako rešava pomoću
dinamičkog programiranja
Tajno pismo (3)
DP[i][j] ->
Koliko postoji reči dužine i čija je suma karaktera j?
Dodelimo vrednosti karakterima na sledeći način:
‘a’ = 0, ‘b’ = 1, ‘c’ = 2, …, ‘z’ = 25
“Ručno” popunjavamo vrednosti
za reči koje se sastoje od samo jednog karaktera,
a zatim dinamički računamo vrednosti za duže reči:
Iteriramo po svim mogućim dužinama reči
Za fiksirano dužinu reči,
iteriramo po svim mogućim sumama karaktera reči te dužine
Za fiksirano dužinu reči i fiksirano sumu karaktera reči
te dužine pokušamo da fiksiramo poslednji karakter
i na vrednost DP[i] dodamo vrednost reči
kraće za jedan karakter i sume manje
za vrednost i-tog karaktera DP[i – 1][j – k]
Tajna poruka (4)
Pseudokod:
for i = 0 to 25 do DP[1][i] ← 1
for i = 2 to 100 do
for j = 0 to 25 * i do
for k = 0 to 25 do
if ( j – k ≥ 0)
DP[i][j] ← DP[i][j] + DP[i – 1][j – k]
if ( DP[i][j] ≥ MOD )
DP[i][j] ← DP[i][j] - MOD
Tajna poruka (5)
Kada smo popunili matricu DP algoritam
za rešavanje ovog problema izgleda ovako:
SUM ← 0
for i = 1 to |S| do SUM ← SUM + S[i]
return DP[|S|][SUM] – 1
Oduzimamo na kraju 1,
jer ne računamo reč S
Korisni linkovi
Dynamic Programming:
From novice to advanced
http://www.topcoder.com/tc?d1=tutorials&
d2=dynProg&module=Static
Ilic A., Ilic A., Uvod u dinamičko
programiranje
Minimum edit distance:
https://www.stanford.edu/class/cs124/lec/
med.pdf
MIT, Introduction to algorithms

You might also like