Denimo, da imamo funkcijo f, ki računa zelo počasi, zato želimo shranjevati rezultate za kasnejšo uporabo in si s tem prihraniti ponovno računanje.
Definicijo funkcije f lahko popravimo tako, da uporablja slovar. Denimo, da imamo:
def f(x):
    y = ⟨zamuden-računski-postopek>
    return y
Izboljšamo jo takole:
d = {} # slovar do sedaj izračunanih vrednosti x ↦ f(x)
def f(x):
    if x in d:
        # rezultat je že spravljen v slovarju
        return d[x]
    else:
        # rezulat moramo izračunati
        y = ⟨zamuden-računski-postopek>
        d[x] = y # spravimo v slovar za nadaljno uporabo
        return y
Postopku, ki funkcijo f predela v enakovredno funkcijo, ki hrani do sedaj izračunane rezultate, pravimo memoizacija.
Memoizacije nam ni treba vsakič implementirati na roko, v ta namen lahko definiramo funkcijo memo, ki sprejme funkcijo f in vrne njeno memoizirano inačico.
def memo(f):
    """memo(f) sprejme funkcijo f in vrne njeno memoizirano inačico."""
    cache = {}
    # Pomožna funkcija, ki deluje tako kot f, le da pogleda v cache, preden
    # izračuna rezultat. V Pythonu *args pomeni "vsi argumenti, kolikor jih pač je".
    # Na ta način lahko memoiziramo funkcije, ki sprejmejo poljubno število argumentov.
    def g(*args):
        if args in cache:
            return cache[args]
        else:
            y = f(*args)
            cache[args] = y
            return y
    return g
Funkcijo f sedaj memoiziramo tako, da pokličemo memo(f). Primere najdete v memo_primeri.py.
Funkcija memo ne deluje dobro na rekurzivni funkcijah, ker ne prestreže rekurzivnih klicev, glej primer fib v memo_primeri.py.
Če želimo, da memoizacija pravilno deluje na rekurzivni funkciji f, to naredimo takole:
def f(x):
  ....
f = memo(f)
To isto lahko dosežemo z dekoratorjem.
Prava rešitev so Pythonovi dekoratorji. Ti omogočajo, da definicijo funkcije f “dekoriramo” s funkcijo memo in takoj dobimo memoiziran f. Splošna oblika je
@dekorator
def f(...):
   ...
Fibonaccijeva števila z dekoratorjem definiramo takole:
@memo
def fibo(n):
    if n == 0 or n == 1:
        return 1
    else:
        return fibo(n-1) + fibo(n-2)
Poleg teba bodo rekurzivni klici poklicali memoizirano funkcijo, kar smo želeli.
V memo_primeri.py je še primer memoizacije funkcije dveh argumentov, ki računa binomske koeficiente.
Spoznali smo že me algoritme tipa deli & vladaj, pri katerih problem delimo na manjše dele, ki jih rešimo rekurzivno z istim postopkom in njihove rešitve sestavimo skupaj.
Lahko se zogdi, da se podprobeli pri deli & vladaj ponavljajo. V tem primeru jih je nesmiselno znova in znova reštevati, raje njihove rešitve memoiziramo.
Z dinamičnim programiranjem lahko rešujemo optimizacijske probleme, ki imajo naslednje lastnosti:
P, ki ga lahko rešimo na več načinov. Na primer: “poišči pot od Ljubljane do Maribora”.Kadar je zadoščeno zgornjim pogojem, lahko uporabimo metodo, ki se zaradi zgodvonskih razlogov imenuje dinamično programiranje:
Kako to deluje, se najbolje vidi na primerih.
Tat krade izdelke iz trgovine. S seboj je prinesel nahrbtnik, v katetega daje nakradene izdelke. Vsak izdelek ima vrednost in velikost. Katere izdelke naj ukrade tat, da bodo imeli čim večjo skupno vrednost in ne bodo presegali kapacitete nahrbtnika?
Izdelke označimo 0, 1, 2, …, n-1. Vrednost i-tega naj bo vrednostᵢ in velikost velikostᵢ. Ali imamo opravka z dinamičnim programiranjem? Da, vendar moramo premisliti, kaj so podproblemi.
Naj bo V(i,k) problem: Poišči izbor izdelkov i, i+1, …, n-1 ki ne presega skupne velikosti k in ima največjo možno skupno vrednost.
Naj bo v(i,k) vrednost optimalne rešitve problema V(i,k).
Preverimo, da veljajo pogoji za dinamično programiranje:
Problem V(i, k) je osnovni primer je je i ≥ n (ni več izdelkov na vojo) ali k = 0 (ni več prostora).
Za i < n in k > 0 lahko V(i, k) razbijemo na podprobleme:
če je velkostᵢ > k je V(i, k) = V(i+1, k) ker je i-ti izdelek prevelik in ga ne vzamemo
sicer V(i, k) razbijemo na podproblema V(i+1, k) (izdelka i ne vzamemo) in V(i+1, k-velikostᵢ) (izdelek i damo v nahrbtnik).
Načelo optimalnosti velja:
 v(i, k) = 0 če je i ≥ n ali k = 0
 v(i, k) = v(i+1, k) če je velikostᵢ > k
 v(i, k) = max (v(i+1,k), vrednostᵢ + v(i+1, k - velikostᵢ)Rešitev najdemo v nahrbtnik.py.
Dan je aciklični graf G = (V, E):
V je množica vozliščE je množica usmerjenih povezavZa vsako povezavo (u,v) ∈ E je dana razdalja d(u,v) > 0 med njima.
Naloga: poisči najkrajšo pot po grafu med dvema danima vozliščema.
Naj bo P(x,y) najkrajša pot od vozlišča x do vozlišča y in ℓ(x,y) njena dolžina. Preverimo pogoje dinamičnega programiranja:
P(x,y) je osnovni problem, če je x = y. Velja ℓ(x,x) = 0.
P(x,y) razbijemo na podprobleme: za vsako povezavo x → z rešimo podproblem P(z,x). Če poznamo najkrajše poti od sosedov vozlišča x do y, potem lahko iz njih izračunamo najkrajšo pod od x do y.
Načelo optimalnosti: če najkrajša pot od x do y vodi preko soseda z, potem je najkrajša tudi od z do y. Velja rekurzivna zveza:
 ℓ(x,x) = 0
 ℓ(x,y) = min { d(x,z) + ℓ(z,y) | (x,z) ∈ E }Rešitev najdemo v najkrajsa_pot.py.