JavaRush /Курсы /Модуль 1: Python Core /Основы динамического программирования

Основы динамического программирования

Модуль 1: Python Core
19 уровень , 4 лекция
Открыта

5.1 Определение динамического программирования

Динамическое программирование (Dynamic Programming, DP) — это метод оптимизации, который используется для решения сложных задач путём разбиения их на более простые подзадачи. Динамическое программирование эффективно используется для задач, которые можно разбить на перекрывающиеся подзадачи с оптимальной структурой подзадач.

Принципы работы:

1. Оптимальная подструктура:

Задача обладает оптимальной подструктурой, если её оптимальное решение можно построить из оптимальных решений её подзадач. Это означает, что можно решить большую задачу, решив несколько меньших подзадач.

2. Перекрывающиеся подзадачи:

Задача имеет перекрывающиеся подзадачи, если её подзадачи повторяются несколько раз в процессе решения задачи. Динамическое программирование эффективно решает задачи с перекрывающимися подзадачами путём запоминания результатов уже решённых подзадач (мемоизация).

3. Мемоизация и табуляция:

Мемоизация (сверху вниз): Рекурсивный подход, при котором результаты подзадач сохраняются в памяти для избежания повторных вычислений.

Табуляция (снизу вверх): Итеративный подход, при котором результаты подзадач вычисляются и сохраняются в таблице (обычно массиве) и используются для вычисления конечного результата.

Примеры задач, решаемых методом динамического программирования

5.2 Задача о рюкзаке

Задача:

Имеется набор предметов, каждый с весом и стоимостью. Нужно выбрать предметы для рюкзака с ограниченной грузоподъемностью, чтобы максимизировать общую стоимость.

Важно! В отличии от аналогичной задачи, которая хорошо решалась жадным алгоритмом, в этой задаче дробить предметы нельзя.

Решение:

Создается таблица, где строки соответствуют предметам, а столбцы — возможной вместимости рюкзака. Значение в ячейке представляет максимальную стоимость для данного числа предметов и вместимости.

Временная сложность: O(n * W), где n — количество предметов, W — вместимость рюкзака.

Пример кода на Python:


def knapsack(weights, values, W):
    n = len(weights)
    dp = [[0] * (W + 1) for _ in range(n + 1)]
            
    for i in range(1, n + 1):
        for w in range(W + 1):
            if weights[i - 1] <= w:
                dp[i][w] = max(dp[i - 1][w], dp[i - 1][w - weights[i - 1]] + values[i - 1])
            else:
                dp[i][w] = dp[i - 1][w]
            
    return dp[n][W]
        
        

5.3 Задача о нахождении наименьшего пути

Задача:

Найти кратчайшие пути между всеми парами вершин взвешенного графа.

Решение:

Используется таблица, где строки и столбцы соответствуют вершинам графа. Значение в ячейке представляет кратчайшее расстояние между двумя вершинами.

Временная сложность: O(V^3), где V — количество вершин

Пример кода на Python:


def floyd_warshall(graph):
    v = len(graph)
    dist = list(map(lambda i: list(map(lambda j: j, i)), graph))
            
    for k in range(v):
        for i in range(v):
            for j in range(v):
                dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j])
            
    return dist
        
        

5.4 Сравнение динамического программирования с другими методами

Сравнение с методом грубой силы (brute force):

Временная сложность:

  • Грубая сила: часто имеет экспоненциальную временную сложность (например, O(2^n) для задачи Фибоначчи).
  • Динамическое программирование: уменьшает временную сложность до полиномиальной (например, O(n) для задачи Фибоначчи).

Пространственная сложность:

  • Грубая сила: может использовать меньше памяти, но временные затраты высоки.
  • Динамическое программирование: может использовать больше памяти для хранения промежуточных результатов (например, O(n) для задачи Фибоначчи с мемоизацией), но выигрывает во времени.

Сравнение с методом жадных алгоритмов (greedy algorithms):

Оптимальность:

  • Жадные алгоритмы: не всегда находят глобально оптимальное решение, но работают быстрее и проще в реализации.
  • Динамическое программирование: находит глобально оптимальное решение, но требует больше вычислительных ресурсов.

Примеры задач:

  • Жадные алгоритмы: задача о рюкзаке с дробями (fractional knapsack), минимальное остовное дерево (MST).
  • Динамическое программирование: задача о рюкзаке (целочисленная), наибольшая общая подпоследовательность (LCS).
2
Задача
Модуль 1: Python Core, 19 уровень, 4 лекция
Недоступна
Задача о рюкзаке
Задача о рюкзаке
2
Задача
Модуль 1: Python Core, 19 уровень, 4 лекция
Недоступна
Все дороги ведут в Рим
Все дороги ведут в Рим
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ