Быстрый поиск наибольшей возрастающей подпоследовательности — различия между версиями
(→Оптимизация до O(n\operatorname{log}\operatorname{log}k): Подраздел "Основная идея" написан) |
(→Деление на блоки: Подраздел написал) |
||
Строка 176: | Строка 176: | ||
=== Деление на блоки === | === Деление на блоки === | ||
==== Основная идея ==== | ==== Основная идея ==== | ||
− | Разделим исходную перестановку <tex>\pi</tex> на блоки <tex>C_j = {\pi_{(j-1)m + 1},~\pi_{(j-1)m + 2},~\dots, ~\pi_{(j-1)m + m}}</tex> | + | Разделим исходную перестановку <tex>\pi</tex> на блоки <tex>C_j = \{\pi_{(j-1)m + 1},~\pi_{(j-1)m + 2},~\dots, ~\pi_{(j-1)m + m}\}</tex>. |
+ | |||
+ | Получим сортированные варианты этих блоков <tex>C_j^S</tex>. При лобовой {{Acronym|цифровой|radix}} сортировке мы получим <tex>O(\frac{n}{m}n)</tex>. Дополним каждый элемент <tex>\pi</tex> номером блока, в котором он находится и смещением в этом блоке. Теперь, {{Acronym|рассматривая номер блока как старший разряд, элемент как младший разряд|по смещению внутри блока не сортируем}}, можно сортировать цифровой сортировкой за линейное время <tex>O(n)</tex>. | ||
+ | ==== Пример ==== | ||
+ | Пусть <tex>m = 5</tex>. Исходно: | ||
+ | {| class="wikitable" style="text-align:center" | ||
+ | | Блок ||style="background:#FF8080"|1||style="background:#FF8080"|1||style="background:#FF8080"|1||style="background:#FF8080"|1||style="background:#FF8080"|1||style="background:#80FF80"|2||style="background:#80FF80"|2||style="background:#80FF80"|2||style="background:#80FF80"|2||style="background:#80FF80"|2||style="background:#8080FF"|3||style="background:#8080FF"|3 | ||
+ | |- | ||
+ | |<tex>\pi</tex>||style="background:#FF8080"|9||style="background:#FF8080"|3||style="background:#FF8080"|10||style="background:#FF8080"|4||style="background:#FF8080"|8||style="background:#80FF80"|1||style="background:#80FF80"|2||style="background:#80FF80"|12||style="background:#80FF80"|6||style="background:#80FF80"|5||style="background:#8080FF"|7||style="background:#8080FF"|11 | ||
+ | |- | ||
+ | |Смещение||style="background:#FF8080"|1||style="background:#FF8080"|2||style="background:#FF8080"|3||style="background:#FF8080"|4||style="background:#FF8080"|5||style="background:#80FF80"|1||style="background:#80FF80"|2||style="background:#80FF80"|3||style="background:#80FF80"|4||style="background:#80FF80"|5||style="background:#8080FF"|1||style="background:#8080FF"|2 | ||
+ | |} | ||
+ | После сортировки: | ||
+ | {| class="wikitable" style="text-align:center" | ||
+ | |Блок ||style="background:#FF8080"|1||style="background:#FF8080"|1||style="background:#FF8080"|1||style="background:#FF8080"|1||style="background:#FF8080"|1||style="background:#80FF80"|2||style="background:#80FF80"|2||style="background:#80FF80"|2||style="background:#80FF80"|2||style="background:#80FF80"|2||style="background:#8080FF"|3||style="background:#8080FF"|3 | ||
+ | |- | ||
+ | |<tex>\pi</tex> ||style="background:#FF8080"|3||style="background:#FF8080"|4||style="background:#FF8080"|8||style="background:#FF8080"|9||style="background:#FF8080"|10||style="background:#80FF80"|1||style="background:#80FF80"|2||style="background:#80FF80"|5||style="background:#80FF80"|6||style="background:#80FF80"|12||style="background:#8080FF"|7||style="background:#8080FF"|11 | ||
+ | |- | ||
+ | |Смещение ||style="background:#FF8080"|2||style="background:#FF8080"|4||style="background:#FF8080"|5||style="background:#FF8080"|1||style="background:#FF8080"|3||style="background:#80FF80"|1||style="background:#80FF80"|2||style="background:#80FF80"|5||style="background:#80FF80"|4||style="background:#80FF80"|3||style="background:#8080FF"|1||style="background:#8080FF"|2 | ||
+ | |} |
Версия 23:51, 6 января 2017
Задача: |
Дана перестановка . Требуется найти НВП за , где — длина НВП. |
Содержание
Алгоритм
Нахождение длины НВП
Основная идея
Пусть
— входная перестановка.Для каждой длины элемент, что может быть последним в возрастающей подпоследовательности длины , запишем их в массив .
предполагаемой НВП находим наименьшийЕсли обрабатываемый элемент
больше последнего элемента какой-нибудь возрастающей последовательности, он может ее увеличить.Будем последовательно обрабатывать элементы
:- Если , значит с ним можно сделать максимальную, из уже рассмотренных, возрастающую подпоследовательность. Записываем его в конец больше
- Иначе заменяет наименьший лучший элемент, из тех, что больше .
Следует отметить, что полученный массив также образует возрастающую последовательность, где мы должны выполнять операции приоритетную очередь, реализованную через Дерево ван Эмде Боаса. Таким образом получаем амортизированного времени на одну операцию.
, соответственно целесообразно использоватьПример
Типы операций:
Последовательность:
9 | 3 | 10 | 4 | 8 | 1 | 2 | 12 | 6 | 5 | 7 | 11 |
Состояние очереди при каждом добавлении:
9 | 9 | ||||
3 | 3 | ||||
3 | 10 | 10 | |||
3 | 4 | 4 | |||
3 | 4 | 8 | 8 | ||
1 | 4 | 8 | 1 | ||
1 | 2 | 8 | 2 | ||
1 | 2 | 8 | 12 | 12 | |
1 | 2 | 6 | 12 | 6 | |
1 | 2 | 5 | 12 | 5 | |
1 | 2 | 5 | 7 | 7 | |
1 | 2 | 5 | 7 | 11 | 11 |
Псевдокод
int LIS(vector<int> следующий B.delete(B.next(x)) else // добавленный элемент - максимальный // предыдущие значения не трогаем, очередь увеличилась k = k + 1 return k) PriorityQueue B // рабочая приоритетная очередь int k = 0 // длина НВП int n = .size for i = 1 to n x = [i] // в любом случае добавляем в очередь очередной элемент // устаревшие будем удалять B.insert(x) if B.next(x) // добавленный элемент — не максимальный // удаляем предыдущее значение — заменяем
Расширение алгоритма до нахождения НВП
Основная идея
Будем запоминать пары: для каждого элемента записываем его "предшественника".
Тогда, выбрав какой-нибудь лучший элемент для максимальной длины, мы можем легко восстановить НВП .
Общий вид алгоритма
9 | 9 | ||||
3 | 3 | ||||
3 | 10 | 10 | |||
3 | 4 | 4 | |||
3 | 4 | 8 | 8 | ||
1 | 4 | 8 | 1 | ||
1 | 2 | 8 | 2 | ||
1 | 2 | 8 | 12 | 12 | |
1 | 2 | 6 | 12 | 6 | |
1 | 2 | 5 | 12 | 5 | |
1 | 2 | 5 | 7 | 7 | |
1 | 2 | 5 | 7 | 11 | 11 |
predecessor | |||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
1 | 3 | 2 | 2 | 5 | 4 | 3 | 7 | 8 |
Псевдокод
vector<int> LIS(vector<int>) PriorityQueue B int k = 0 int n = .size vector<int> predecessor(n) // резервируем позиций for i = 1 to n x = [i] B.insert(x) predecessor[x] = B.prev(x) if B.next(x) B.delete(B.next(x)) else k = k + 1 // по цепочке от последнего элемента // восстанавливаем НВП vector<int> result int cur = B.max() result += [cur] while predecessor[cur] result += [predecessor[cur]] cur = predecessor[cur] return result
Оптимизация до
Основная идея
Чтобы Дерево ван Эмде Боаса выполняло операции за , необходимо алфавит обрабатываемых значений уменьшить до .
Предположим, мы знаем такое приближение . Если мы разобьем всю последовательность на блоки из и нам удастся обрабатывать каждый как перестановку из элементов элементов, то мы получим асимптотическое время , а т.к. , то . (Мы будем обрабатывать блоки последовательно, т.е. с предыдущего блока у нас может остаться значений в очереди, которые дополняются значениями очередного блока - получаем верхнее ограничение в обрабатываемых возможных значений.)
Описанный здесь алгоритм подбора
и получение асимптотической оценки в других подразделах рассмотрено не будет, т.к. в основном это доказательство, сложного для понимания/реализации ничего нетРассмотрим последовательность
, где , - некоторое значение, меньшее .Будем последовательно для элементов этой последовательности запускать алгоритм. Если условие перестает выполняться, прерываем выполнение. Таким образом, время работы для каждого будет . Найдется такой , который окажется больше , и алгоритм успешно завершится.
Общее время работы - . Заметим, что , т.к. в противном случае , что противоречит тому, что - первый из тех, что больше . Следовательно, .
Получаем время работы
Деление на блоки
Основная идея
Разделим исходную перестановку
на блоки .Получим сортированные варианты этих блоков цифровой сортировке мы получим . Дополним каждый элемент номером блока, в котором он находится и смещением в этом блоке. Теперь, рассматривая номер блока как старший разряд, элемент как младший разряд, можно сортировать цифровой сортировкой за линейное время .
. При лобовойПример
Пусть
. Исходно:Блок | 1 | 1 | 1 | 1 | 1 | 2 | 2 | 2 | 2 | 2 | 3 | 3 |
9 | 3 | 10 | 4 | 8 | 1 | 2 | 12 | 6 | 5 | 7 | 11 | |
Смещение | 1 | 2 | 3 | 4 | 5 | 1 | 2 | 3 | 4 | 5 | 1 | 2 |
После сортировки:
Блок | 1 | 1 | 1 | 1 | 1 | 2 | 2 | 2 | 2 | 2 | 3 | 3 |
3 | 4 | 8 | 9 | 10 | 1 | 2 | 5 | 6 | 12 | 7 | 11 | |
Смещение | 2 | 4 | 5 | 1 | 3 | 1 | 2 | 5 | 4 | 3 | 1 | 2 |