diff --git a/12-dp/obdelnik-12.py b/12-dp/obdelnik-12.py
new file mode 100644
index 0000000000000000000000000000000000000000..dab435116364966051be40b19dceeedc00463b24
--- /dev/null
+++ b/12-dp/obdelnik-12.py
@@ -0,0 +1,34 @@
+#!/usr/bin/python3
+# Počet dláždění obdélníka 1×n kostkami 1×1 a 1×2
+
+# Rekurzivní funkce s exponenciální složitostí
+def d(n):
+    if n <= 1:
+        return 1
+    return d(n-1) + d(n-2)
+
+# Všimli jsme si, že d() počítá spoustu podproblémů opakovaně,
+# tak jsme dodělali kešování známých mezivýsledků. Tím se časová
+# složitost zlepšila na O(n), ale chvíli trvá to dokázat.
+pamet = { 0: 1, 1: 1 }
+def d2(n):
+    if n not in pamet:
+        pamet[n] = d2(n-1) + d2(n-2)
+    return pamet[n]
+
+# Tady počítáme tytéž podproblémy od nejmenšího k největšímu.
+# Rekurzi jsme nahradili obyčejným cyklem, složitost je evidentně O(n).
+def d3(n):
+    D = [1, 1] + [0]*n
+    for i in range(2, n+1):
+        D[i] = D[i-1] + D[i-2]
+    return D[n]
+
+# Konečně jsme si všímli, že není potřeba pamatovat si všechny
+# mezivýsledky, ale stačí předchozí dva. Tím jsme snížili prostorovou
+# složitost na konstantní.
+def d4(n):
+    a = b = 1
+    for i in range(2, n+1):
+        a, b = a+b, a
+    return a
diff --git a/12-dp/obdelnik-1k.py b/12-dp/obdelnik-1k.py
new file mode 100644
index 0000000000000000000000000000000000000000..20322a1326277261bb395a49bae98df58993564a
--- /dev/null
+++ b/12-dp/obdelnik-1k.py
@@ -0,0 +1,7 @@
+#!/usr/bin/python3
+# Počet dláždění obdélníka 1×n kostkami 1×k (kde k je libovolné)
+
+def d(n):
+    if n == 0:
+        return 1
+    return sum(d(i) for i in range(n))
diff --git a/12-dp/rozklad-na-slova.py b/12-dp/rozklad-na-slova.py
new file mode 100644
index 0000000000000000000000000000000000000000..b4fd1aba889372d2574afa295e18241967754c4c
--- /dev/null
+++ b/12-dp/rozklad-na-slova.py
@@ -0,0 +1,82 @@
+#!/usr/bin/python3
+# Rozklad řetězce na posloupnost slov ze slovníku.
+
+# Načítání slovníku ze souboru
+
+slovnik = set()
+
+def nacti_slovnik():
+    with open('slovnik') as f:
+        for slovo in f:
+            slovnik.add(slovo.strip())
+
+# První pokus: rekurzivní rozkládání. Zkoušíme všechny možnosti,
+# jaké slovo může být první, a pro každou z nich rekurzivně zjistíme,
+# zda se zbytek řetězce dá rozložit. V nejhorším případě to trvá
+# exponenciálně dlouho (například pro slovník {"a","aa"} a řetězec
+# ze samých a-ček je tato úloha ekvivalentní s rozkladem obdélníka,
+# který jsme už zkoumali).
+def rozloz(retezec):
+    if retezec == "":
+        return True
+    for i in range(len(retezec)+1):
+        if retezec[:i] in slovnik and rozloz(retezec[i:]):
+                return True
+    return False
+
+# Všimli jsme si, že všechny podproblémy jsou suffixy původního řetězce,
+# takže jako parametr místo řetězce předáváme pozici v původním řetězci
+# (začátek suffixu). Také bychom si mohli všimnout (ale ještě jsme toho
+# nevyužili), že možných podproblémů je málo a začít kešovat.
+def rozloz2(retezec):
+
+    def roz(p):
+        """Rozlož retezec[p:]"""
+        if p == len(retezec):
+            return True
+        return any(retezec[p:q] in slovnik and roz(q) for q in range(p+1, len(retezec)+1))
+
+    return roz(0)
+
+# Podproblémy řešíme od nejkratšího suffixu k nejdelšímu, rekurzi jsme
+# nahradili cyklem. Časová složitost činí O(n³).
+def rozloz3(retezec):
+
+    n = len(retezec)
+    umim = [None]*(n+1)
+    umim[n] = True
+
+    for p in range(n-1, -1, -1):
+        umim[p] = any(retezec[p:q] in slovnik and umim[q] for q in range(p+1, n+1))
+
+    return umim[0]
+
+# A teď nás zajímá nejen, jestli rozklad existuje, ale také jak nějaký konkrétní
+# rozklad vypadá. Pro každý suffix si zapamatujeme, které slovo (stačí jeho délka)
+# fungovalo jako první v suffixu. Podle toho pak rozklad sestrojíme.
+def rozloz4(retezec):
+
+    n = len(retezec)
+    umim = [None]*(n+1)
+    umim[n] = 0
+
+    # Hlavní výpočet
+    for p in range(n-1, -1, -1):
+        for q in range(p+1, n+1):
+            if retezec[p:q] in slovnik and umim[q] is not None:
+                umim[p] = q-p
+
+    # Rekonstrukce rozkladu
+    if umim[0] is None:
+        return None
+    else:
+        rozklad = []
+        p = 0
+        while p < n:
+            rozklad.append(retezec[p:p+umim[p]])
+            p += umim[p]
+        return rozklad
+
+    return umim[0]
+
+nacti_slovnik()