Быстрый поиск наибольшей возрастающей подпоследовательности — различия между версиями
Shersh (обсуждение | вклад) м (→Псевдокод) |
м |
||
| Строка 18: | Строка 18: | ||
* Если <tex>\pi(i)</tex> больше {{Acronym | <tex>\pi(1), \pi(2),~\dots~,\pi(i-1)</tex> | всех уже полученных значений B }}, значит с ним можно сделать максимальную, из уже рассмотренных, возрастающую подпоследовательность. Записываем его в конец <tex>B</tex> | * Если <tex>\pi(i)</tex> больше {{Acronym | <tex>\pi(1), \pi(2),~\dots~,\pi(i-1)</tex> | всех уже полученных значений B }}, значит с ним можно сделать максимальную, из уже рассмотренных, возрастающую подпоследовательность. Записываем его в конец <tex>B</tex> | ||
| − | * Иначе <tex>\pi(i)</tex> | + | * Иначе <tex>\pi(i)</tex> заменяет наименьший лучший элемент, из тех, что больше <tex>\pi(i)</tex>. |
Следует отметить, что полученный массив также образует возрастающую последовательность, где мы должны выполнять операции <tex>insert, next, delete</tex>, соответственно целесообразно использовать [[ Приоритетные очереди | приоритетную очередь]], реализованную через [[Дерево ван Эмде Боаса]]. Таким образом получаем <tex>O(\operatorname{log}\operatorname{log} n)</tex> амортизированного времени на одну операцию. | Следует отметить, что полученный массив также образует возрастающую последовательность, где мы должны выполнять операции <tex>insert, next, delete</tex>, соответственно целесообразно использовать [[ Приоритетные очереди | приоритетную очередь]], реализованную через [[Дерево ван Эмде Боаса]]. Таким образом получаем <tex>O(\operatorname{log}\operatorname{log} n)</tex> амортизированного времени на одну операцию. | ||
| Строка 67: | Строка 67: | ||
==== Псевдокод ==== | ==== Псевдокод ==== | ||
<code> | <code> | ||
| − | ''' | + | '''int''' LIS('''vector<int>''' <tex>\pi</tex>) |
| − | B | + | '''PriorityQueue''' B <font color=darkgreen>// рабочая приоритетная очередь</font> |
| − | k = 0 <font color=darkgreen>// длина НВП</font> | + | '''int''' k = 0 <font color=darkgreen>// длина НВП</font> |
| − | n = <tex>\pi</tex>.size | + | '''int''' n = <tex>\pi</tex>.size |
'''for''' i = 1 to n | '''for''' i = 1 to n | ||
x = <tex>\pi</tex>[i] | x = <tex>\pi</tex>[i] | ||
| Строка 76: | Строка 76: | ||
<font color=darkgreen>// устаревшие будем удалять</font> | <font color=darkgreen>// устаревшие будем удалять</font> | ||
B.insert(x) | B.insert(x) | ||
| − | '''if''' B.next(x) | + | '''if''' <tex>\exists</tex> B.next(x) |
<font color=darkgreen>// добавленный элемент — не максимальный</font> | <font color=darkgreen>// добавленный элемент — не максимальный</font> | ||
<font color=darkgreen>// удаляем предыдущее значение — заменяем {{Acronym |следующий|минимальный из бóльших}}</font> | <font color=darkgreen>// удаляем предыдущее значение — заменяем {{Acronym |следующий|минимальный из бóльших}}</font> | ||
| Строка 132: | Строка 132: | ||
==== Псевдокод ==== | ==== Псевдокод ==== | ||
<code> | <code> | ||
| − | ''' | + | '''vector<int>''' LIS('''vector<int>''' <tex>\pi</tex>) |
| − | B | + | '''PriorityQueue''' B |
| − | k = 0 | + | '''int''' k = 0 |
| − | n = <tex>\pi</tex>.size | + | '''int''' n = <tex>\pi</tex>.size |
| − | <font color=blue>predecessor | + | <font color=blue>'''vector<int>''' predecessor(n)</font> <font color=darkgreen>// резервируем <tex>n</tex> позиций</font> |
'''for''' i = 1 to n | '''for''' i = 1 to n | ||
x = <tex>\pi</tex>[i] | x = <tex>\pi</tex>[i] | ||
B.insert(x) | B.insert(x) | ||
<font color=blue>predecessor[x] = B.prev(x)</font> | <font color=blue>predecessor[x] = B.prev(x)</font> | ||
| − | '''if''' B.next(x) | + | '''if''' <tex>\exists</tex> B.next(x) |
B.delete(B.next(x)) | B.delete(B.next(x)) | ||
'''else''' | '''else''' | ||
k = k + 1 | k = k + 1 | ||
| − | <font color=darkgreen>//по цепочке от последнего элемента | + | <font color=darkgreen>// по цепочке от последнего элемента |
| − | //восстанавливаем НВП</font> | + | // восстанавливаем НВП</font> |
| − | <font color=blue>result | + | <font color=blue>'''vector<int>''' result |
| − | cur = B.max() | + | '''int''' cur = B.max() |
result += [cur] | result += [cur] | ||
| − | '''while''' predecessor[cur] | + | '''while''' <tex>\exists</tex> predecessor[cur] |
result += [predecessor[cur]] | result += [predecessor[cur]] | ||
cur = predecessor[cur] | cur = predecessor[cur] | ||
'''return''' result</font> | '''return''' result</font> | ||
</code> | </code> | ||
| − | + | == Оптимизация до <tex>O(n\operatorname{log}\operatorname{log}k)</tex> == | |
| − | == | + | === Деление на блоки === |
Версия 03:04, 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> ) PriorityQueue B // рабочая приоритетная очередь int k = 0 // длина НВП int n = .size for i = 1 to n x = [i] // в любом случае добавляем в очередь очередной элемент // устаревшие будем удалять B.insert(x) if B.next(x) // добавленный элемент — не максимальный // удаляем предыдущее значение — заменяем следующий B.delete(B.next(x)) else // добавленный элемент - максимальный // предыдущие значения не трогаем, очередь увеличилась k = k + 1 return k
Расширение алгоритма до нахождения НВП
Основная идея
Будем запоминать пары: для каждого элемента записываем его "предшественника".
Тогда, выбрав какой-нибудь лучший элемент для максимальной длины, мы можем легко восстановить НВП .
Общий вид алгоритма
| 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



