Динамическое программирование (ДП) решает каждую повторяющуюся подзадачу один раз и запоминает ответ. Экспоненциальная рекурсия превращается в полиномиальное заполнение таблицы.

Когда работает ДП

  • Оптимальная подструктура: оптимальный ответ большой задачи строится из оптимальных ответов меньших.
  • Перекрывающиеся подзадачи: одна и та же маленькая задача возникает снова и снова.

В «разделяй и властвуй» части независимы, а здесь повторяются. От жадного алгоритма ДП отличается тем, что учитывает все ответы подзадач и не требует доказательства безопасности локального выбора.

Дерево вызовов fib(5): повторяющиеся подзадачи выделены красным
Дерево вызовов fib(5): повторяющиеся подзадачи выделены красным

Наивная рекурсия экспоненциальна

python
calls = 0

def fib(n):
    global calls
    calls += 1
    if n < 2:
        return n
    return fib(n - 1) + fib(n - 2)

print(fib(20), calls)
calls = 0
fib(25)
print("fib(25) calls:", calls)

fib(20) делает 21 891 вызов, а fib(25) — 242 785: примерно O(1.6^n).

Сверху вниз: мемоизация