Участник:Artem.ustinov/НВП — различия между версиями
(→Состояние очереди при каждом добавлении) |
(→Пример) |
||
| Строка 259: | Строка 259: | ||
| style="background:#FFCC00"| 1 || || || style="background: #77A9F4"| 1 || style="background: #9ACD32"| 3 | | style="background:#FFCC00"| 1 || || || style="background: #77A9F4"| 1 || style="background: #9ACD32"| 3 | ||
|-align="center" | |-align="center" | ||
| − | | 1 || style="background:#FFCC00"| 5 || || style="background: #77A9F4"| 5 || style="background: #9ACD32"| 10 | + | | <tex>1</tex> || style="background:#FFCC00"| 5 || || style="background: #77A9F4"| 5 || style="background: #9ACD32"| 10 |
|-align="center" | |-align="center" | ||
| − | | 1 || style="background:#FFCC00"| 2 || || style="background: #77A9F4"| 2 || style="background: #9ACD32"| 4 | + | | <tex>1</tex> || style="background:#FFCC00"| 2 || || style="background: #77A9F4"| 2 || style="background: #9ACD32"| 4 |
|-align="center" | |-align="center" | ||
| − | | 1 || 2 || style="background:#FFCC00"| 3 || style="background: #77A9F4"| 3 || style="background: #9ACD32"| 8 | + | | <tex>1</tex> || <tex>2</tex> || style="background:#FFCC00"| 3 || style="background: #77A9F4"| 3 || style="background: #9ACD32"| 8 |
|} | |} | ||
| Строка 284: | Строка 284: | ||
| colspan="3"|<tex>B</tex> | | colspan="3"|<tex>B</tex> | ||
|-align="center" | |-align="center" | ||
| − | | 3||4||8 | + | | <tex>3</tex>||<tex>4</tex>||<tex>8</tex> |
|} | |} | ||
| || | | || | ||
| Строка 291: | Строка 291: | ||
| colspan="5"|<tex>C_2^s</tex> | | colspan="5"|<tex>C_2^s</tex> | ||
|-align="center" | |-align="center" | ||
| − | | 1||2||5||6||12 | + | | <tex>1</tex>||<tex>2</tex>||<tex>5</tex>||<tex>6</tex>||<tex>12</tex> |
|} | |} | ||
| || | | || | ||
| Строка 298: | Строка 298: | ||
| colspan="9"|<tex>\mathtt{merged}</tex> | | colspan="9"|<tex>\mathtt{merged}</tex> | ||
|-align="center" | |-align="center" | ||
| − | |!\pi|1||2||3||4||5||6||8||12 | + | |!\pi|<tex>1</tex>||<tex>2</tex>||<tex>3</tex>||<tex>4</tex>||<tex>5</tex>||<tex>6</tex>||<tex>8</tex>||<tex>12</tex> |
|-align="center" | |-align="center" | ||
| − | |!key|1||2||3||4||5||6||7||8 | + | |!key|<tex>1</tex>||<tex>2</tex>||<tex>3</tex>||<tex>4</tex>||<tex>5</tex>||<tex>6</tex>||<tex>7</tex>||<tex>8</tex> |
|} | |} | ||
|} | |} | ||
| Строка 329: | Строка 329: | ||
| style="background:#FFCC00"| 3 || || || style="background: #77A9F4"| 3 | | style="background:#FFCC00"| 3 || || || style="background: #77A9F4"| 3 | ||
|-align="center" | |-align="center" | ||
| − | | 3 || style="background:#FFCC00"| 4 || || style="background: #77A9F4"| 4 | + | | <tex>3</tex> || style="background:#FFCC00"| 4 || || style="background: #77A9F4"| 4 |
|-align="center" | |-align="center" | ||
| − | | 3 || 4 || style="background:#FFCC00"| 7 || style="background: #77A9F4"| 7 | + | | <tex>3</tex> || <tex>4</tex> || style="background:#FFCC00"| 7 || style="background: #77A9F4"| 7 |
|} | |} | ||
| Строка 338: | Строка 338: | ||
! <tex>B_1</tex>||<tex>B_2</tex>||<tex>B_3</tex>||<tex>B_4</tex>||<tex>key</tex>||<tex>\pi</tex> | ! <tex>B_1</tex>||<tex>B_2</tex>||<tex>B_3</tex>||<tex>B_4</tex>||<tex>key</tex>||<tex>\pi</tex> | ||
|-align="center" | |-align="center" | ||
| − | | style="background:#FFCC00"| 1 || 4 || 7 || || style="background: #77A9F4"| 1 || style="background: #9ACD32"| 1 | + | | style="background:#FFCC00"| 1 || <tex>4</tex> || <tex>7</tex> || || style="background: #77A9F4"| 1 || style="background: #9ACD32"| 1 |
|-align="center" | |-align="center" | ||
| − | | 1 || style="background:#FFCC00"| 2 || 7 || || style="background: #77A9F4"| 2 || style="background: #9ACD32"| 2 | + | | <tex>1</tex> || style="background:#FFCC00"| <tex>2</tex> || <tex>7</tex> || || style="background: #77A9F4"| 2 || style="background: #9ACD32"| 2 |
|-align="center" | |-align="center" | ||
| − | | 1 || 2 || 7 || style="background:#FFCC00"| 8 || style="background: #77A9F4"| 8 || style="background: #9ACD32"| 12 | + | | <tex>1</tex> || <tex>2</tex> || <tex>7</tex> || style="background:#FFCC00"| 8 || style="background: #77A9F4"| 8 || style="background: #9ACD32"| 12 |
|-align="center" | |-align="center" | ||
| − | | 1 || 2 || style="background:#FFCC00"| 6 || 8 || style="background: #77A9F4"| 6 || style="background: #9ACD32"| 6 | + | | <tex>1</tex> || <tex>2</tex> || style="background:#FFCC00"| 6 || <tex>8</tex> || style="background: #77A9F4"| 6 || style="background: #9ACD32"| 6 |
|-align="center" | |-align="center" | ||
| − | | 1 || 2 || style="background:#FFCC00"| 5 || 8 || style="background: #77A9F4"| 5 || style="background: #9ACD32"| 5 | + | | <tex>1</tex> || <tex>2</tex> || style="background:#FFCC00"| 5 || <tex>8</tex> || style="background: #77A9F4"| 5 || style="background: #9ACD32"| 5 |
|} | |} | ||
| Строка 367: | Строка 367: | ||
| colspan="4"|<tex>B</tex> | | colspan="4"|<tex>B</tex> | ||
|-align="center" | |-align="center" | ||
| − | | 1||2||5||12 | + | | <tex>1</tex>||<tex>2</tex>||<tex>5</tex>||<tex>12</tex> |
|} | |} | ||
| || | | || | ||
| Строка 374: | Строка 374: | ||
| colspan="2"|<tex>C_3^s</tex> | | colspan="2"|<tex>C_3^s</tex> | ||
|-align="center" | |-align="center" | ||
| − | | 7||11 | + | | <tex>7</tex>||<tex>11</tex> |
|} | |} | ||
| || | | || | ||
| Строка 381: | Строка 381: | ||
| colspan="6"|<tex>\mathtt{merged}</tex> | | colspan="6"|<tex>\mathtt{merged}</tex> | ||
|-align="center" | |-align="center" | ||
| − | |!\pi|1||2||5||7||11||12 | + | |!\pi|<tex>1</tex>||<tex>2</tex>||<tex>5</tex>||<tex>7</tex>||<tex>11</tex>||<tex>12</tex> |
|-align="center" | |-align="center" | ||
| − | |!key|1||2||3||4||5||6 | + | |!key|<tex>1</tex>||<tex>2</tex>||<tex>3</tex>||<tex>4</tex>||<tex>5</tex>||<tex>6</tex> |
|} | |} | ||
|} | |} | ||
| Строка 414: | Строка 414: | ||
| style="background:#FFCC00"| 1 || || || || style="background: #77A9F4"| 1 | | style="background:#FFCC00"| 1 || || || || style="background: #77A9F4"| 1 | ||
|-align="center" | |-align="center" | ||
| − | | 1 || style="background:#FFCC00"| 2 || || || style="background: #77A9F4"| 2 | + | | <tex>1</tex> || style="background:#FFCC00"| 2 || || || style="background: #77A9F4"| 2 |
|-align="center" | |-align="center" | ||
| − | | 1 || 2 || style="background:#FFCC00"| 3 || || style="background: #77A9F4"| 3 | + | | <tex>1</tex> || <tex>2</tex> || style="background:#FFCC00"| 3 || || style="background: #77A9F4"| 3 |
|-align="center" | |-align="center" | ||
| − | | 1 || 2 || 3 || style="background:#FFCC00"| 6 || style="background: #77A9F4"| 6 | + | | <tex>1</tex> || <tex>2</tex> || <tex>3</tex> || style="background:#FFCC00"| 6 || style="background: #77A9F4"| 6 |
|} | |} | ||
| Строка 425: | Строка 425: | ||
! <tex>B_1</tex>||<tex>B_2</tex>||<tex>B_3</tex>||<tex>B_4</tex>||<tex>B_5</tex>||<tex>key</tex>||<tex>\pi</tex> | ! <tex>B_1</tex>||<tex>B_2</tex>||<tex>B_3</tex>||<tex>B_4</tex>||<tex>B_5</tex>||<tex>key</tex>||<tex>\pi</tex> | ||
|-align="center" | |-align="center" | ||
| − | | 1 || 2 || 3 || style="background:#FFCC00"| 4 || || style="background: #77A9F4"| 4 || style="background: #9ACD32"| 7 | + | | <tex>1</tex> || <tex>2</tex> || <tex>3</tex> || style="background:#FFCC00"| 4 || || style="background: #77A9F4"| 4 || style="background: #9ACD32"| 7 |
|-align="center" | |-align="center" | ||
| − | | 1 || 2 || 3 || 4 || style="background:#FFCC00"| 5 || style="background: #77A9F4"| 5 || style="background: #9ACD32"| 11 | + | | <tex>1</tex> || <tex>2</tex> || <tex>3</tex> || <tex>4</tex> || style="background:#FFCC00"| 5 || style="background: #77A9F4"| 5 || style="background: #9ACD32"| 11 |
|} | |} | ||
Результат завершения алгоритма: | Результат завершения алгоритма: | ||
| Строка 436: | Строка 436: | ||
Получаем, что длина НВП — <tex>5</tex>, и НВП оканчивается на <tex>\mathtt{merged}[5]=11</tex>. | Получаем, что длина НВП — <tex>5</tex>, и НВП оканчивается на <tex>\mathtt{merged}[5]=11</tex>. | ||
| − | |||
'''Восстановление НВП''' | '''Восстановление НВП''' | ||
| Строка 442: | Строка 441: | ||
! colspan="12"| <tex>\mathtt{predecessor}</tex> | ! colspan="12"| <tex>\mathtt{predecessor}</tex> | ||
|-align="center" | |-align="center" | ||
| − | | style="background:#DCFFFF"|1||style="background:#DCDCFF"|2||3||4||style="background:#DCFFDC"|5||6||style="background:#FFDCDC"|7||8||9||10||style="background:#FFFFC8"|11||12 | + | | style="background:#DCFFFF"|<tex>1</tex>||style="background:#DCDCFF"|<tex>2</tex>||<tex>3</tex>||<tex>4</tex>||style="background:#DCFFDC"|<tex>5</tex>||<tex>6</tex>||style="background:#FFDCDC"|<tex>7</tex>||<tex>8</tex>||<tex>9</tex>||<tex>10</tex>||style="background:#FFFFC8"|<tex>11</tex>||<tex>12</tex> |
|-align="center" | |-align="center" | ||
| − | |style="background:#FFFFC8"| ||style="background:#DCFFFF"|1|| ||3||style="background:#DCDCFF"|2||style="background:#E6E6FF"|2||style="background:#DCFFDC"|5||4|| ||3||style="background:#FFDCDC"|7||8 | + | |style="background:#FFFFC8"| ||style="background:#DCFFFF"|<tex>1</tex>|| ||<tex>3</tex>||style="background:#DCDCFF"|<tex>2</tex>||style="background:#E6E6FF"|<tex>2</tex>||style="background:#DCFFDC"|<tex>5</tex>||<tex>4</tex>|| ||<tex>3</tex>||style="background:#FFDCDC"|<tex>7</tex>||<tex>8</tex> |
|} | |} | ||
Начинаем восстановление с <tex>\mathtt{merged}[5] = 11</tex>: | Начинаем восстановление с <tex>\mathtt{merged}[5] = 11</tex>: | ||
{| class="wikitable" style="center" | {| class="wikitable" style="center" | ||
|-align="center" | |-align="center" | ||
| − | | обратный порядок||style="background:#FFFFC8"|11||style="background:#FFDCDC"|7||style="background:#DCFFDC"|5||style="background:#E6E6FF"|2||style="background:#DCFFFF"|1 | + | | обратный порядок||style="background:#FFFFC8"|<tex>11</tex>||style="background:#FFDCDC"|<tex>7</tex>||style="background:#DCFFDC"|<tex>5</tex>||style="background:#E6E6FF"|<tex>2</tex>||style="background:#DCFFFF"|<tex>1</tex> |
|-align="center" | |-align="center" | ||
| − | | НВП||style="background:#DCFFFF"|1||style="background:#E6E6FF"|2||style="background:#DCFFDC"|5||style="background:#FFDCDC"|7||style="background:#FFFFC8"|11 | + | | НВП||style="background:#DCFFFF"|<tex>1</tex>||style="background:#E6E6FF"|<tex>2</tex>||style="background:#DCFFDC"|<tex>5</tex>||style="background:#FFDCDC"|<tex>7</tex>||style="background:#FFFFC8"|<tex>11</tex> |
|} | |} | ||
Версия 18:35, 1 января 2018
| Задача: |
| Дана перестановка множества . Требуется найти НВП за , где — длина НВП. |
Содержание
Алгоритм за O(n log log n)
Нахождение длины НВП
Основная идея
Пусть — входная перестановка.
Будем последовательно обрабатывать элементы в порядке
Для каждой длины предполагаемой НВП находим наименьший элемент, который может быть последним в возрастающей подпоследовательности длины и запишем его в массив . Будем называть его наилучшим элементом для длины .
- Если больше каждого элемента , вычисленного для подпоследовательности , тогда с ним можно сделать возрастающую подпоследовательность максимальной длины из уже рассмотренных, в которой он будет последним элементом. Значит, записываем его в конец .
- Иначе будет наилучшим элементом для уже существующей длины, тогда мы находим наименьшее и заменяем элементом .
Следует отметить, что полученный массив также образует возрастающую последовательность, на котором мы должны выполнять операции , соответственно целесообразно использовать приоритетную очередь, реализованную через Дерево ван Эмде Боаса. Так как данная структура данных производит описанные операции за , где k — количество бит чисел, которые позволяет хранить дерево, то полученный алгоритм работает за , потому что все элементы последовательности не превосходят n.
Доказательство оптимальности
| Утверждение: |
Пусть — входная перестановка. В результате описанного алгоритма размер массива равен длине НВП последовательности |
|
Докажем, что перед обработкой и после обработки элемента последовательности алгоритмом сохраняется инвариант, что в массиве хранятся наилучшие элементы для каждой возможной длины возрастающих подпоследовательностей обработанной последовательности.
|
Пример
Типы операций
- Добавление элемента, который больше всех предыдущих:
- Замещение элемента более подходящим, т.е. добавление немаксимального элемента:
Пример последовательности
Состояние очереди при каждом добавлении
| 9 | 9 | ||||
| 3 | 3 | ||||
| 10 | 10 | ||||
| 4 | 4 | ||||
| 8 | 8 | ||||
| 1 | 1 | ||||
| 2 | 2 | ||||
| 12 | 12 | ||||
| 6 | 6 | ||||
| 5 | 5 | ||||
| 7 | 7 | ||||
| 11 | 11 |
Псевдокод
int LIS([n]) PriorityQueue B // рабочая приоритетная очередь int k = 0 // длина НВП for i = 1 to n x = [i] // в любом случае добавляем в очередь очередной элемент // устаревшие будем удалять B.insert(x) if B.next(x) // добавленный элемент — не максимальный // удаляем следующее за x значение B.delete(B.next(x)) else // добавленный элемент — максимальный // предыдущие значения не трогаем, очередь увеличилась k = k + 1 return k
Расширение алгоритма до нахождения НВП
Основная идея
Будем запоминать пары: для каждого элемента записываем его "предшественника".
Тогда, пройдя по предшественникам, начиная с последнего элемента очереди , мы можем восстановить НВП.
Общий вид алгоритма
| 9 | |||||
| 3 | |||||
| 10 | |||||
| 4 | |||||
| 8 | |||||
| 1 | 1 | ||||
| 1 | 2 | 2 | |||
| 2 | 12 | ||||
| 2 | 6 | ||||
| 2 | 5 | 5 | |||
| 5 | 7 | 7 | |||
| 7 | 11 | 11 |
| predecessor | |||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
Псевдокод
int[] LIS([n]) PriorityQueue B int k = 0 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 // по цепочке от последнего элемента // восстанавливаем НВП int result[k] int cur = B.max for i = k - 1 downto 0 result[i] = cur cur = predecessor[cur] return result
Оптимизация до O(n log log k)
Чтобы Дерево ван Эмде Боаса выполняло операции за , необходимо алфавит обрабатываемых значений уменьшить до .
Предположим, мы знаем такое приближение числа числом . Мы обсудим, как найти такое позже.
Чтобы достичь нужной оценки, будем делить последовательность на блоки длины , кроме последнего, который может быть меньше, и обрабатывать каждый блок отдельно.
Деление на блоки
Последовательность делится на блоки элементов:
Обозначим за отсортированный блок . Отсортированные и неотсортированные блоки будем хранить в памяти.
Цифровая сортировка каждых блоков отдельно будет давать нам время рваботы . Чтобы отсортировать их за линейное время, дополним каждый элемент номером его блока и получим пары . Цифровая сортировка этих пар, если принимать за старший разряд номер блока, а за младший значение элемента, будет работать за , потому что значения элементов и номера блоков не превосходят .
Обработка блока
Обрабатывая блоки, будем работать не со значениями элементов, а с ключами, которые определенны для каждого элемента внутри блоков. Все блоки будут обрабатываться онлайн, то есть мы не перейдём к обработке следующего блока, пока не закончим с текущим.
Каждому элементу взаимно однозначно сопоставим ключ . Все значения ключей расположим в промежутке , и в очереди будем работать со значениями ключей элементов.
Чтобы определить ключи элементов так, чтобы их значения были в представленном промежутке, будем, работая с блоком , сливать элементы, ключи которых находятся в очереди , с в список . Получим ключи, сопоставив каждому элементу в его позицию в этом списке. Как было замечено ранее, элементы, чьи ключи находятся в располагаются в возрастающем порядке, поэтому возможно производить тривиальную операцию слияния. Поскольку мы предположили, что , то количество ключей в не больше , тогда длина не больше , что позволяет однозначно определить ключи на множестве .
После того, как мы определили новые ключи для элементов, обновим ключи в очереди .
Затем запускаем описанный выше алгоритм , для ключей элементов в порядке исходной последовательности.
В итоге, обработка блока делится на следующие этапы:
- Достаем из очереди ключи , конвертируем их в элементы и кладём в список .
- Сливаем элементы в со следующим отсортированным блоком в список .
- Присваеваем новые ключи элементам в порядке списка .
- Вставляем в новые ключи элементов списка .
- Обрабатываем ключи элементов блока в порядке исходной последовательности с помощью алгоритма . Для восстановления НВП также используем массив "предшественников", который будет работать с соответствующими ключами элементов .
Доказательство оптимальности
| Утверждение: |
Пусть имеется последовательность , разбитая на блоков длины . В результате описанного выше алгоритма получается очередь , размер которой равен длине НВП последовательности . |
|
Докажем, что перед обработкой блока и после его обработки сохраняется инвариант, что очередь хранит ключи наилучших элементов для всех возможных длин возрастающих подпоследовательностей обработанной последовательности элементов.
|
Пример
Предположим, что . Исходно получаем:
| Блок | 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 | 1 | 1 | 1 | 1 | 2 | 2 | 2 | 2 | 2 | 3 | 3 |
| 3 | 4 | 8 | 9 | 10 | 1 | 2 | 5 | 6 | 12 | 7 | 11 |
Первый блок
|
| ||||||||||||||||||||||||||||||||||||||
Обработка блока с помощью алгоритма .
| 4 | 4 | 9 | ||
| 1 | 1 | 3 | ||
| 5 | 5 | 10 | ||
| 2 | 2 | 4 | ||
| 3 | 3 | 8 |
В результате получаем
Второй блок
Восстанавливаем элементы из : .
Сливаем и восстановленные элементы из :
|
|
|
| ||||||||||||||||||||||||||||||||||||||||||||
|
| ||||||||||||||||||||||||||||||||||||||
Обновляем ключи в очереди:
| 3 | 3 | ||
| 4 | 4 | ||
| 7 | 7 |
запускаем для блока:
| 1 | 1 | 1 | |||
| 2 | 2 | ||||
| 8 | 8 | 12 | |||
| 6 | 6 | 6 | |||
| 5 | 5 | 5 |
В результате получаем:
Третий блок
Восстанавливаем элементы из : .
Сливаем и восстановленные элементы из :
|
|
|
| |||||||||||||||||||||||||||||||||
Обновление старых ключей:
запускаем для блока:
Результат завершения алгоритма:
Получаем, что длина НВП — , и НВП оканчивается на . Восстановление НВП Начинаем восстановление с :
Оценка времени работыТак как размер списка не больше , а количество блоков всего . То общее количество присваиваний новых ключей элементам последовательности, также как и количество операций слияния списков, не больше , где c — некоторая константа. Каждая операция с приоритетной очередью требует времени, так как элементы в не больше . Рассмотрим последовательность , где , — некоторое значение, меньшее . Будем по порядку для элементов этой последовательности запускать алгоритм, представленный выше. Если размер очереди становится больше , то условие перестает выполняться, тогда останавливаем алгоритм и переходим к следующему значению . Когда найдётся первое , то алгоритм успешно завершится. Таким образом, время работы запущенного алгоритма — для . Заметим, что .
Общее время работы алгоритма по всем — . Обратим внимание, что , так как в противном случае , что противоречит тому, что — первый из тех, которые больше . Следовательно, . Тогда алгоритм также работает за время . См. также
Источники информации |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||



