Учебные материалы: механизм вместо тезисов, блоки «Факты для карточек», Ловушки (Claude Code) + diag/cards_src.tsv
This commit is contained in:
@@ -1,5 +1,40 @@
|
||||
# Задача 05 — длиннейшая возрастающая подпоследовательность (C++)
|
||||
|
||||
## Глава I. Общая информация
|
||||
|
||||
- **Цель:** посчитать длину строго возрастающей подпоследовательности массива за
|
||||
O(n log n), а не за наивные O(n²).
|
||||
- **Почему это в Eltex:** задача — эталон на «знаешь ли ты приём tails + бинарный поиск
|
||||
поверх DP», а сам паттерн «поддерживать отсортированный массив минимально возможных хвостов
|
||||
и подставлять новый элемент через `lower_bound`» переиспользуется в задачах на планирование
|
||||
и в сравнении версий (diff-подобные алгоритмы ищут именно длиннейшие совпадающие/растущие
|
||||
подпоследовательности).
|
||||
- **Что сдаётся:** `solution.cpp`, проходящий `python3 grade.py 05`.
|
||||
|
||||
## Глава II. Что нужно знать до старта
|
||||
|
||||
Наивное решение — DP: `dp[i]` = длина LIS, заканчивающейся на элементе `i`, пересчитывается
|
||||
перебором всех `j < i` — O(n²), при 100 000 элементах это ~10¹⁰ операций и тест не пройдёт
|
||||
(ограничение — 2 секунды).
|
||||
|
||||
Быстрое решение — «хвосты» (patience sorting): массив `tails`, где `tails[k]` — минимально
|
||||
возможный последний элемент возрастающей подпоследовательности длины `k+1`, накопленной по
|
||||
уже просмотренному префиксу. Массив `tails` всегда отсортирован по построению, поэтому для
|
||||
каждого нового `x` бинарным поиском (`lower_bound`) находится первая позиция с
|
||||
`tails[pos] >= x`: если такая позиция есть — `x` заменяет значение в ней (подпоследовательность
|
||||
той же длины, но с меньшим хвостом — выгоднее для будущих продолжений); если позиции нет —
|
||||
`x` дописывается в конец, увеличивая длину LIS на 1. Именно `lower_bound`, а не `upper_bound` —
|
||||
строгое возрастание требует заменить первый элемент `>= x`, а не `> x` (иначе повторяющиеся
|
||||
значения ошибочно продлевают подпоследовательность).
|
||||
|
||||
Итоговая длина `tails` в конце обработки массива и есть `lis_length`. Значения в `tails` —
|
||||
это не сама LIS, только корректная её длина.
|
||||
|
||||
Почему дальше: тот же приём «поддерживай отсортированный инвариант + `lower_bound`» стоит
|
||||
знать и для задач на минимальное число возрастающих подпоследовательностей на весь массив.
|
||||
|
||||
## Глава III. Задание
|
||||
|
||||
```c++
|
||||
int lis_length(const std::vector<int>& a); // длина НВП (строго возрастающей)
|
||||
```
|
||||
@@ -11,4 +46,29 @@ int lis_length(const std::vector<int>& a); // длина НВП (строго
|
||||
- подпоследовательность не обязана быть непрерывной.
|
||||
|
||||
Проверка: `python3 grade.py 05`. Критерий: все `ok`, сборка без предупреждений.
|
||||
Разбор после сдачи: «хвосты» — массив минимальных последних элементов + lower_bound.
|
||||
|
||||
## Глава IV. Ловушки
|
||||
|
||||
- `upper_bound` вместо `lower_bound` при строгом возрастании → повторяющиеся значения
|
||||
ошибочно продлевают подпоследовательность → длина LIS завышена на массивах с дубликатами
|
||||
(например, `[1, 1, 1]` должен дать 1, а не 3).
|
||||
- Наивная DP-версия за O(n²) → проходит маленькие тесты, но на 100 000 элементах превышает
|
||||
лимит в 2 секунды → `grade.py 05` роняет прогон по таймауту, а не по неверному ответу.
|
||||
- Не обработан пустой вектор отдельным веткой → если код полагается на то, что цикл по
|
||||
пустому диапазону сам вернёт 0, это обычно верно, но стоит явно проверить — тихая логическая
|
||||
ошибка здесь не кидает исключение, просто даёт неверный ответ на пограничном тесте.
|
||||
|
||||
**Факты для карточек**
|
||||
- base | Сложность наивного DP-решения LIS? — O(n²)
|
||||
- core | Сложность решения через `tails` + бинарный поиск? — O(n log n)
|
||||
- core | Что хранит `tails[k]`? — минимально возможный последний элемент возрастающей
|
||||
подпоследовательности длины `k+1`
|
||||
- deep | Почему для строгого возрастания нужен `lower_bound`, а не `upper_bound`? —
|
||||
`lower_bound` находит первый элемент `>= x` и заменяет его, не давая повторам продлевать
|
||||
подпоследовательность
|
||||
|
||||
## После сдачи
|
||||
|
||||
Разбор: почему массив `tails` не является самой LIS, а только хранит её длину через
|
||||
минимальные возможные хвосты; как по `tails` при необходимости восстановить саму
|
||||
подпоследовательность (дополнительный массив предшественников).
|
||||
|
||||
Reference in New Issue
Block a user