Динамика (18 ноября 2019)
- bitset
- Подробный рассказ bitset: что может, как это делает.
- Оптимизируем рюкзак до nS/w
- Пояснение про асимптотику: w=64, но также w ≥ logn, значит можно писать под O(...)
- Оптимизируем в НОП память до n2/w (восстановление ответа всё ещё доступно).
- Напоминание про НОП, повторение Хиршберга
- Мы умеем получать O(n) память без восстановления ответа, храня два последних слоя (идея слоёв ещё нужна)
- Мы умеем получать O(n) память с восстановлением ответа: алгоритм Хиршберга
- НВП (Наибольшая Возрастающая Подпоследовательность) за nlogn
- Динамика за O(n2): len[i] → max, конец ровно в i, переход i → j
- Общее состояние описанного выше процесса "идём слева направо, берём числа в ответ" : [j, len, last=a[i]] − дошли до j, взяли len чисел, последнее взятое i
- Динамика за O(n2): last[j,len] → min. Ход динамики: j↑, слой last[j] → слой last[j+1]
- Замечаем свойства массива last[j]: возрастает, отличается от last[j+1] только в одной точке
- Оптимизируем до O(nlogn), реализация:
for (int x : a) *lower_bound(last,last+n,x) = x
- DP − измельчение перехода, выбор состояния, функции (на примере задачи про погрузку корабля)
- Условие: есть массив грузов (a[i]), по очереди грузятся корабли (все размера W), корабль = взять что-то слева + взять что-то справа. Погрузить всё мин числом кораблей.
- Переинтепретация: идём с двух краёв, набираем рюкзаки, разбить предметы на минимальное число рюкзаков.
- Решение #1: O(n4), k[L,R] → min, переход = перебрать префикс и суффикс
- Решение #2: O(n3), k[L,R] → min, переход = перебрать только префикс, суффикс вторым указателем
- Решение #3: O(n3), w[L,R,k] → min, переход или закончить погрузку, или взять ещё один предмет (сделали измельчение)
- Решение #4: O(n2), pair [L,R] → min, переход или закончить погрузку, или взять ещё один предмет (перенесли часть состояния в функцию)
- Заметим, что к решению #4 применимы оптимизации по памяти (и "хранить последние две строки" и алгоритм Хиршберга)
- (На практику!) Ещё одна история про выбор состояния: рюкзак со стоимостями, веса большие, стоимости маленькие.
− Перерыв −
- DP и возведение матрицы в степень
- Пути в графе (количество путей длины ровно k)
- Числа фибоначчи (начали на практике)
- Рекуррентные соотношения за O(k3 log n) (начали на практике)
- Реклама: рекуррентные соотношения можно решать и за O(k log k log n)
- DP и два указателя (серверы за O(n2)) без док-ва
- Постановка задачи: разбить массив длины n на k отрезков
- Естественная динамика minCost[k,n], переход − отделить k-й отрезок, внутри перехода ставим оптимальный сервер на отрезок
- Постановка оптимального сервера на отрезок − взвешенная медиана. Можно сделать предподсчёт server[l,r]
- Также известна стоимость отрезка [l,r], она выражается через частичные суммы весов prefixSum[r+1], prefixSum[l], prefixSum[server[l,r]]
- Получили решение за O(n2k).
- Оптимизация Кнута: заменим перебор 0 ≤ p[k,n] ≤ n-1 на p[k-1,n] ≤ p[k,n] ≤ p[k,n+1]
- Разделяй и властвуй: используем p[k,n] ≤ p[k,n+1], делаем переход p[k] → p[k+1] на отрезке [l,r]: p[k,l] ≤ p[k,i] ≤ p[r,i], посчитаем за O(r-l) p[k,m], сделаем 2 рекурсивных вызова.