Поделиться страницей

Изучите X за Y минут

Где X=Dynamic Programming

Динамическое программирование

Введение

Динамическое программирование (dynamic programming, DP) — мощный инструмент для решения определенного класса задач. Идея очень проста: если вы решили задачу для каких-то вводных данных, сохраните этот результат для будущих вычислений, чтобы снова не решать ту же самую задачу с теми же данными.

Запомните! «Кто не помнит своего прошлого, обречен на то, чтобы пережить его вновь»

Способы решения подобных задач

  1. Сверху-вниз: Начните с разбиения задачи на подзадачи. Если вы видите, что подзадача уже была решена, тогда используйте сохраненный ранее результат. Иначе решите подзадачу и сохраните её результат. Эта техника интуитивно понятна и называется мемоизацией.

  2. Снизу-вверх: Проанализируйте задачу и определите порядок, в котором решаются подзадачи, и начните решать от тривиальной подзадачи до изначальной задачи. Это гарантирует, что подзадачи будут решены, прежде чем решится вся задача. В этом и заключается динамическое программирование.

Пример задачи динамического программирования

В задаче по определению самой длинной возрастающей подпоследовательности необходимо найти найти самую длинную возрастающую подпоследовательность для заданной последовательности. Для последовательности S={ a1, a2, a3, a4, ............., an-1, an } мы должны найти самое длинное подмножество, такое, что для всех j и i, j<i в подмножестве aj<ai.

Прежде всего, мы должны найти значение самых длинных подпоследовательностей (LSi) для каждого индекса i с последним элементом последовательности, равным ai. Тогда наибольшая LSi будет самой длинной подпоследовательностью в данной последовательности. Для начала LSi равна единице, поскольку ai является элементом последовательности (последний элемент). Затем для всех j таких, что j<i и aj<ai, мы находим наибольшую LSj и добавляем ее к LSi. Тогда алгоритм выполняется за O(n2).

Псевдокод для определения длины самой длинной возрастающей подпоследовательности: сложность этого алгоритма можно уменьшить, если использовать структуру данных получше, а не массив. Использование массива с предшественниками и переменной largest_sequences_so_far («наибольшие последовательности на данный момент») и ее индекса сэкономит много времени.

Аналогичная концепция может быть применена для определения самого длинного пути в направленном ациклическом графе.

for i=0 to n-1
    LS[i]=1
    for j=0 to i-1
        if (a[i] >  a[j] and LS[i]<LS[j])
            LS[i] = LS[j]+1
for i=0 to n-1
    if (largest < LS[i])

Некоторые известные задачи DP

Онлайн-ресурсы


Хотите предложить свой перевод? Может быть, улучшение перевода? Откройте Issue в репозитории GitHub или сделайте pull request сами!

Первоначально предоставлено автором Akashdeep Goel, и обновлено 1 автором.