Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
__Динамическое программирование_специалитет.doc
Скачиваний:
0
Добавлен:
01.05.2025
Размер:
2.74 Mб
Скачать

Примеры задач, решаемых при помощи динамического программирования. Наибольшая общая подпоследовательность. (ноп, Longest Common Subsequence, lcs)

Рассмотрим последовательность чисел a1, a2, …, an. Рассмотрим теперь еще одну последовательность b1, b2, …, bm. Требуется найти длину самой длинной подпоследовательности последовательности {ai}, которая одновременно является и подпоследовательностью последовательности {bi}. Такую последовательность называют наибольшей общей подследовательностью (НОП). Например, для последовательностей 1, 2, 3, 4, 5 и 2, 7, 3, 2, 5 НОП является подпоследовательность 2, 3, 5, состоящая из трёх членов.

Решение

Для решения задачи методом динамического программирования опишем подзадачи, на которые будем разбивать задачу. Построим функцию LCS(p, q), которая находит длину НОП для двух начальных участков a1, …, ap и b1, …, bq наших последовательностей. Пусть для всех пар q и p (p < n, q < m), мы задачу решать уже научились. Попробуем вычислить LCS(n, m). Рассмотрим два случая:

1) an= bm. Тогда LCS(n, m)=LCS(n-1, m-1)+1.

2) an≠ bm. Тогда LCS(n, m)=max(LCS(n, m-1), LCS(n-1, m)).

Пользуясь этими формулами, мы можем заполнить таблицу значений LCS(p, q) для всех p и q последовательно: сначала заполняем первую строчку слева направо, затем вторую и т.д. Последнее число в последней строке и будет ответом на поставленную задачу. Данный алгоритм требует порядка O(nm) операций.

Примеры задач, решаемых при помощи динамического программирования. Наибольшая возрастающая подпоследовательность. (нвп, Longest Increasing Subsequence, lis)

Дана последовательность a1, …, an. Требуется найти ее возрастающую подпоследовательность наибольшей длины.

Решение

Первый способ

Сделаем копию данной последовательности и отсортируем ее по возрастанию. Тогда НВП будет также НВП для отсортированной последовательности. Поэтому достаточно найти наибольшую общую подпоследовательность исходной и отсортированной последовательности. Обратите внимание: на самом деле таким способом мы найдем не возрастающую, а неубывающую подпоследовательность. Чтобы найти возрастающую подпоследовательность, нужно сделать еще один шаг. Сложность этого алгоритма – O(n2).

Второй способ

Попробуем воспользоваться методом динамического программирования. Пусть мы знаем НВП для последовательности a1, …, ak. Можем ли мы найти НВП для последовательности a1, …, ak+1? Кажется, что это легко сделать: если ak+1 больше, чем последний член ранее найденной НВП, то ak+1 нужно добавить в НВП, в противном случае нужно оставить НВП неизменной. К сожалению, такое решение оказывается неверным. Рассмотрим такой пример: 1 2 3 6 4 5. Для первых пяти членов этой последовательности длина НВП равна 4, но есть две различных НВП: 1 2 3 6 и 1 2 3 4. Пусть мы выбрали первую из них. Тогда на следующем шаге мы не сможем дополнить ее числом 5 и получим более короткую НВП (1 2 3 6) для всей последовательности, чем должны был получить (1 2 3 4 5). Модифицируем предложенный алгоритм. Будем теперь запоминать не произвольную НВП, а ту, которая заканчивается на наименьшую возможную цифру. К сожалению, и этот способ не приведет нас к успеху. Рассмотрим такой пример: 1 4 2 3. Для последовательности из первых двух символов НВП будет иметь длину 2 (1 4). Но при добавлении символов длина НВП не увеличится, поскольку все оставшиеся числа меньше четырех. Для решения задачи нам потребуется более сложная система подзадач. Пусть для последовательности a1, a2, …, ak мы знаем возрастающие последовательности длины 1, 2, 3, ... Причем среди всех возрастающих последовательностей данной длины мы будем хранить ту, которая оканчивается на меньшее число. Тогда для последовательности a1, a2, …, ak+1 мы сможем построить аналогичный набор последовательностей.

Третий способ

Будем постепенно строить возрастающие цепочки и в каждый момент времени для каждого элемента ai хранить длину самой длинной построенной цепочки, заканчивающейся этим элементом. Приведенный алгоритм имеет сложность O(n2).

Четвертый способ

Будем поочередно обрабатывать члены исходной последовательности. Пусть после обработки k-го элемента нам известно, что возрастающая подпоследовательность длины 1 может оканчиваться числом e[1], ВП длины 2 может оканчиваться числом e[2] и т.д., причем указанные числа e[i] – наименьшие возможные. Заметим, что е[1] < e[2] < e[3] <… Рассмотрим теперь число ak+1. Найдем такое i, что e[i-1] ≤ ak+1 < e[i] (если изначально положить e[0] = -∞, а все остальные члены массива e равными +∞, то такое всегда найдется). Положим e[i]:= ak+1, а остальные элементы массива e[i] оставим без изменений. Заметим, что сложность поиска в упорядоченном массиве есть O(log m), где m – длина НВП, поэтому сложность приведенного алгоритма – О(n log m).

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

Пусть имеется N узлов с номерами 1, 2, …, N. Пусть tij>0 – время прохождения от узла i к узлу j. Пусть N – целевой узел, а ui – минимальное время перехода от узла i к узлу N. Очевидно, что uN=0. По принципу оптимальности получим:

ui = minji(tij + uj), i = 1, 2, …, N-1.

Рассмотрим некоторые свойства полученных уравнений. Пусть (u1, u2, …, uN) и (U1, U2, …, UN) – два решения и m – индекс, для которого максимальна разность

Uj – uj, j = 1, …, N.

Пусть

Um = tmn + Un  tmr + Ur

um = tmr + ur  Um - um  Ur - ur

Так как m – индекс для которого U – u = max, то получаем знаки равенства. Точно также можно найти s, sm, sr и Um – um = Ur – ur = Us – us  UN – uN = 0.

Метод итераций. Пусть

ui(0) = tiN, tNN = 0,

если нет пути от i к N, то положим tiN = . Рассмотрим итерационный процесс:

ui(1) = minij(tij + uj(0)), uN(1) = 0, …

Очевидно, что ui(k+1)  ui(k)). Так как оптимальный путь содержит не более N-2 узла (поскольку нет петель), то ui(N-2) есть решение  число итераций не более N-2.