From 7ede2b028657713fc7d57e2635e7831636bb8fff Mon Sep 17 00:00:00 2001 From: Egor Ivanov <56685477+sibenshtern@users.noreply.github.com> Date: Sun, 7 Jan 2024 03:53:09 +0000 Subject: [PATCH 1/7] Finish first part of theory --- doc/README.md | 2 +- doc/Theory.md | 468 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 469 insertions(+), 1 deletion(-) create mode 100644 doc/Theory.md diff --git a/doc/README.md b/doc/README.md index 755f421..3f3d2ea 100644 --- a/doc/README.md +++ b/doc/README.md @@ -1,6 +1,6 @@ # Теория по алгоритмам: -## 1. Обходы гррафа +## 1. Обходы графа ### 1.1. Ориентированный граф, псевдограф. Неориентированный граф, псевдограф. Связность в неор. графе, компоненты связности. Слабая и сильная связность в ор. графе. Компоненты слабой, сильной связности. ### 1.2. Обход в глубину. Цвета вершин. Времена входа и выхода. Лемма о белых путях(с доказательством). ### 1.3. Проверка связности неориентированного графа. Поиск цикла в неориентированном и ориентированном графе. Топологическая сортировка. diff --git a/doc/Theory.md b/doc/Theory.md new file mode 100644 index 0000000..820f927 --- /dev/null +++ b/doc/Theory.md @@ -0,0 +1,468 @@ +# Теория по алгоритмам + +## Содержание + +- [Теория по алгоритмам ](#теория-по-алгоритмам-) + - [Содержание](#содержание) + - [1. Обходы графа](#1-обходы-графа) + - [1.1. Ориентированный граф, псевдограф. Неориентированный граф, псевдограф. Связность в неориентированном графе, компоненты связности. Слабая и сильная связность в ориентированном графе. Компоненты слабой, сильной связности](#11-ориентированный-граф-псевдограф-неориентированный-граф-псевдограф-связность-в-неориентированном-графе-компоненты-связности-слабая-и-сильная-связность-в-ориентированном-графе-компоненты-слабой-сильной-связности) + - [Ориентированный граф](#ориентированный-граф) + - [Неориентированный граф](#неориентированный-граф) + - [1.2. Обход в глубину. Цвета вершин. Времена входа и выхода. Лемма о белых путях (с доказательством)](#12-обход-в-глубину-цвета-вершин-времена-входа-и-выхода-лемма-о-белых-путях-с-доказательством) + - [Обход в глубину](#обход-в-глубину) + - [Лемма о белых путях](#лемма-о-белых-путях) + - [1.3. Проверка связности неориентированного графа. Поиск цикла в неориентированном и ориентированном графе. Топологическая сортировка](#13-проверка-связности-неориентированного-графа-поиск-цикла-в-неориентированном-и-ориентированном-графе-топологическая-сортировка) + - [Проверка связности неориентированного графа](#проверка-связности-неориентированного-графа) + - [Поиск цикла в неориентированном и ориентированном графе](#поиск-цикла-в-неориентированном-и-ориентированном-графе) + - [Топологическая сортировка](#топологическая-сортировка) + - [1.4. Нахождение компонент сильной связности. Алгоритм Косарайю с доказательством корректности(концепция). Алгоритм Тарьяна без доказательства корректности](#14-нахождение-компонент-сильной-связности-алгоритм-косарайю-с-доказательством-корректностиконцепция-алгоритм-тарьяна-без-доказательства-корректности) + - [Нахождение компонент сильной связности](#нахождение-компонент-сильной-связности) + - [Алгоритм Косарайю с доказательством корректности(концепция)](#алгоритм-косарайю-с-доказательством-корректностиконцепция) + - [Алгоритм Тарьяна без доказательства корректности](#алгоритм-тарьяна-без-доказательства-корректности) + - [1.5. Компоненты реберной двусвязности. Мосты. Поиск мостов](#15-компоненты-реберной-двусвязности-мосты-поиск-мостов) + - [Компоненты реберной двусвязности](#компоненты-реберной-двусвязности) + - [Мосты](#мосты) + - [Поиск мостов](#поиск-мостов) + - [1.6. Компоненты вершинной двусвязности. Точки сочленения. Поиск точек сочленения](#16-компоненты-вершинной-двусвязности-точки-сочленения-поиск-точек-сочленения) + - [Компоненты вершинной двусвязности](#компоненты-вершинной-двусвязности) + - [Точки сочленения](#точки-сочленения) + - [Поиск точек сочленения](#поиск-точек-сочленения) + - [1.7. Волновой алгоритм. Обход в ширину (применение очереди в волновом алгоритме)](#17-волновой-алгоритм-обход-в-ширину-применение-очереди-в-волновом-алгоритме) + - [Волновой алгоритм](#волновой-алгоритм) + - [Обход в ширину (применение очереди в волновом алгоритме)](#обход-в-ширину-применение-очереди-в-волновом-алгоритме) + - [1.8. Критерий существования Эйлерова пути и цикла в ориентированном и неориентированном графе. Поиск эйлерова пути и цикла](#18-критерий-существования-эйлерова-пути-и-цикла-в-ориентированном-и-неориентированном-графе-поиск-эйлерова-пути-и-цикла) + - [Критерий существования Эйлерова пути и цикла в неориентированном графе](#критерий-существования-эйлерова-пути-и-цикла-в-неориентированном-графе) + - [Критерий существования Эйлерова пути и цикла в ориентированном графе](#критерий-существования-эйлерова-пути-и-цикла-в-ориентированном-графе) + - [Поиск эйлерова пути и цикла](#поиск-эйлерова-пути-и-цикла) + +## 1. Обходы графа + +### 1.1. Ориентированный граф, псевдограф. Неориентированный граф, псевдограф. Связность в неориентированном графе, компоненты связности. Слабая и сильная связность в ориентированном графе. Компоненты слабой, сильной связности + +#### Ориентированный граф + +> **Ориентированным графом** (англ. *directed graph*) $G$ называется пара $G = (V, E)$, где $V$ — множество вершин (англ. vertices), а $E \subset V \times V$ — множество рёбер. + +> **Псевдограф(*мультиорграф*)** — это ориентированный граф, в котором разрешены кратные дуги, то есть дуги, имеющие те же начальные и конечные вершины. + +**Псевдографом** $G$ называется упорядоченная пара $G := (V, A)$, в которой $V$ — множество вершин, $A$ — мультимножество упорядоченных пар вершин. Элементы этого множества называются дугами. + +> Ориентированный граф называется **слабо связным**, если соответствующий неориентированный граф является связным; + +> Ориентированный граф называется **сильно связным**, если всякая вершина $v$ достижима из любой другой вершины $v$; + +> **Компонентой сильной связности** ориентированного графа называется максимальный по включению сильно связный подграф. Другими словами, это подграф, любые две вершины которого принадлежат какому-либо циклу, и содержащий все такие циклы для своих вершин. + +> **Компонентой слабой связности** ориентированного графа называют его максимальный слабо связный подграф. + +#### Неориентированный граф + +> **Неориентированным графом** (англ. *undirected graph*) $G$ называется пара $G = (V, E)$, где $V$ — множество вершин, а $E \subset {{v,u} : v,u \in V}$ — множество рёбер. + +Формально, псевдографом (*мультифграфом*) $G$ называется упорядоченная пара $G := (V, E)$, в которой $V$ — множество вершин, $E$ — мультимножество неупорядоченных пар вершин. Элементы этого множества называются рёбрами. + +> Неориентированный граф называется *связным*, если все его вершины достижимы из некоторой вершины (эквивалентно, из любой его вершины). + +> **Компонентой связности** неориентированного графа называется максимальный по включению связный подграф + +### 1.2. Обход в глубину. Цвета вершин. Времена входа и выхода. Лемма о белых путях (с доказательством) + +#### Обход в глубину + +> **Обход в глубину** (поиск в глубину, англ. *Depth-First Search*, *DFS*) — один из основных методов обхода графа, часто используемый для проверки связности, поиска цикла и компонент сильной связности и для топологической сортировки. + +```c++ +#include + +std::vector> graph; // граф +int vertex_count; // число вершин + +std::vector color; // цвет вершины (0, 1, или 2) + +std::vector time_in, time_out; // "времена" захода и выхода из вершины +int dfs_timer = 0; // "таймер" для определения времён + +void dfs (int vertex) { + time_in[vertex] = dfs_timer++; + color[vertex] = 1; + for (int u : graph[vertex]) + if (color[u] == 0) + dfs(u); + color[v] = 2; + time_out[v] = dfs_timer++; +} +``` + +#### Лемма о белых путях + +> Лемма: Не существует такого момента выполнения поиска в глубину, в который бы существовало ребро из черной вершины в белую. + +**Доказательство**: Пусть в процессе выполнения процедуры DFS нашлось ребро из черной вершины $v$ в белую вершину u. Рассмотрим момент времени, когда мы запустили DFS(v). В этот момент вершина $v$ была перекрашена из белого в серый, а вершина u была белая. Далее в ходе выполнения алгоритма будет запущен DFS(u), поскольку обход в глубину обязан посетить все белые вершины, в которые есть ребро из v. По алгоритму вершина $v$ будет покрашена в черный цвет тогда, когда завершится обход всех вершин, достижимых из нее по одному ребру, кроме тех, что были рассмотрены раньше нее. Таким образом, вершина $v$ может стать черной только тогда, когда DFS выйдет из вершины u, и она будет покрашена в черный цвет. Получаем противоречие. + +### 1.3. Проверка связности неориентированного графа. Поиск цикла в неориентированном и ориентированном графе. Топологическая сортировка + +#### Проверка связности неориентированного графа + +Небольшая модификация алгоритма обхода в глубину, в которой будем возвращать количество посещенных вершин. Запустим такой **DFS** от некоторой вершины графа $G$, если его результат равен $|V|$, то мы побывали во всех вершинах графа, а следовательно он связен, иначе какие-то вершины остались непосещенными. Работает алгоритм за $O(|V|+|E|)$ + +```c++ +std::vector> graph; // граф + +// visited — массив цветов вершин +int dfs(int vertex, std::vector visited): + int visitedVertices = 1 + visited[vertex] = true // помечаем вершину как пройденную + for (auto u : graph[vertex]) // проходим по смежным с u вершинам + // проверяем, не находились ли мы ранее в выбранной вершине + if (!visited[v]) + visitedVertices += dfs(v, visited) + return visitedVertices +``` + +#### Поиск цикла в неориентированном и ориентированном графе + +*Условие:* + +Пусть дан ориентированный или неориентированный граф без петель и кратных рёбер. Требуется проверить, является ли он ациклическим, а если не является, то найти любой цикл. + +*Решение:* + +Произведём серию поисков в глубину в графе. Т.е. из каждой вершины, в которую мы ещё ни разу не приходили, запустим поиск в глубину, который при входе в вершину будет красить её в серый цвет, а при выходе - в чёрный. И если поиск в глубину пытается пойти в серую вершину, то это означает, что мы нашли цикл (если граф неориентированный, то случаи, когда поиск в глубину из какой-то вершины пытается пойти в предка, не считаются). + +```c++ +#include +#include + +int n; +std::vector> graph; +std::vector color; +std::vector parent; +int cycle_start, cycle_end; + +bool dfs(int vertex) { + color[vertex] = 1; + for (size_t i = 0; i < graph[vertex].size(); ++i) { + int $to$ = graph[vertex][i]; + if (color[to] == 0) { + parent[to] = vertex; + if (dfs(to)) return true; + } + else if (color[to] == 1) { + cycle_end = vertex; + cycle_start = $to$ ; + return true; + } + } + color[vertex] = 2; + return false; +} + +int main() { + // чтение графа + + parent.assign(n, -1); + color.assign(n, 0); + cycle_start = -1; + for (int i = 0; i < n; ++i) + if (dfs(i)) + break; + + if (cycle_st == -1) + std::cout << "Acyclic" << "\n"; + else { + std::cout << "Cyclic" << "\n"; + std::vector cycle; + cycle.push_back(cycle_start); + for (int vertex = cycle_end; vertex != cycle_start; vertex = parent[vertex]) + cycle.push_back(vertex); + cycle.push_back(cycle_start); + reverse(cycle.begin(), cycle.end()); + for (size_t i = 0; i < cycle.size(); ++i) + std::cout << cycle[i]+1 << " "; + } +} +``` + +#### Топологическая сортировка + +*Условие:* +Дан ориентированный граф с n вершинами и m рёбрами. Требуется перенумеровать его вершины таким образом, чтобы каждое рёбро вело из вершины с меньшим номером в вершину с большим. Иными словами, требуется найти перестановку вершин (топологический порядок), соответствующую порядку, задаваемому всеми рёбрами графа. +(**Топологическая сортировка** - упорядочивание вершин ациклического ориентированного графа согласно частичному порядку, заданному ребрами орграфа на множестве его вершин.) +Топологическая сортировка может быть не единственной (например, если граф — пустой; или если есть три такие вершины `a`, `b`, `c`, что из `a` есть пути в `b` и в `c`, но ни из `b` в `c`, ни из `c` в `b` добраться нельзя). + +Топологической сортировки может не существовать вовсе — если граф содержит циклы (поскольку при этом возникает противоречие: есть путь и из одной вершины в другую, и наоборот). + +```c++ +#include + +int n; // число вершин +std::vector> graph; // граф +std::vector visited; +std::vector $to$ pological_sort; + +void dfs (int vertex) { + visited[vertex] = true; + for (int u : graph[vertex]) + if (!visited[u] == 0) + dfs(u); + $to$ pological_sort.push_back(vertex); +} + +void $to$ pological_sort() { + visited.assign(n, false); + answer.clear(); + for (int i = 0; i < n; ++i) + if (!visited[i]) + dfs(i); + reverse(answer.begin(), answer.end()); +} +``` + +### 1.4. Нахождение компонент сильной связности. Алгоритм Косарайю с доказательством корректности(концепция). Алгоритм Тарьяна без доказательства корректности + +#### Нахождение компонент сильной связности + +*Условие:* + +Дан ориентированный граф $G$, множество вершин которого $V$ и множество рёбер — $E$. Петли и кратные рёбра допускаются. Обозначим через $n$ количество вершин графа, через $m$ — количество рёбер. + +*Решение:* + +Напомним, что **компонентой сильной связности** (*strongly connected component*) называется такое (максимальное по включению) подмножество вершин $C4, что любые две вершины этого подмножества достижимы друг из друга, т.е. для любых $u, $v$ \in C: u \rightarrow v, $v$ \rightarrow u$, где символом $\rightarrow$ здесь и далее мы будем обозначать достижимость, т.е. существование пути из первой вершины во вторую. Понятно, что компоненты сильной связности для данного графа не пересекаются, т.е. фактически это разбиение всех вершин графа. Отсюда логично определение конденсации G как графа, получаемого из данного графа сжатием каждой компоненты сильной связности в одну вершину. Каждой вершине графа конденсации соответствует компонента сильной связности графа G, а ориентированное ребро между двумя вершинами $C_i$ и $C_j$ графа конденсации проводится, если найдётся пара вершин u из $C_i$, из $C_j$, между которыми существовало ребро в исходном графе, т.е. $(u, v)$ из $E$. + +Важнейшим свойством графа конденсации является то, что он ацикличен. Действительно, предположим, что $C \rightarrow C'$, докажем, что $C' \mapsto C$. Из определения конденсации получаем, что найдутся две вершины $u$ из $C$ и $v$ из $C'$, что $u \rightarrow v$. Доказывать будем от противного, т.е. предположим, что $C' \rightarrow C$, тогда найдутся две вершины $u'$ из $C$ и $v'$ из $C'$, что $v' \rightarrow u'$. Но т.к. $u$ и $u'$ находятся в одной компоненте сильной связности, то между ними есть путь; аналогично для $v$ и $v'$. В итоге, объединяя пути, получаем, что $v \rightarrow u$, и одновременно $u \rightarrow v$. Следовательно, $u$ и $v$ должны принадлежать одной компоненте сильной связности, т.е. получили противоречие, что и требовалось доказать + +#### Алгоритм Косарайю с доказательством корректности(концепция) + +Алгоритм Косарайю предназначен для поиска компонент сильной связности в ориентированном графе и состоит из трёх шагов: + +1. Выполнить поиск в глубину (DFS), пока не будут «помечены» все вершины. Вершина считается «помеченной», когда ей присвоено время выхода из рекурсии (см. основные понятия). + +2. Инвертировать исходный граф + +3. Выполнить DFS в порядке убывания пометок вершин. + +Полученные деревья каждого такта DFS последнего шага являются компонентами сильной связности + +**Доказательство корректности алгоритма:** + +Немного уточним, что требуется доказать: Вершины $u$ и $v$ сильно связаны $\Leftrightarrow$ после выполнения алгоритма они принадлежат одному дереву такта DFS. + +Если вершины u и $v$ были сильно связаны в графе $G$, на третьем этапе будет найден путь из одной вершины в другую (по Лемме (Инвертирование рёбер цикла не влияет на его цикличность)), так как на первом шаге был найден путь $u \rightarrow v$, а на третьем - путь $v \rightarrow u$. Это означает, что по окончании алгоритма обе вершины лежат в одном дереве. + +Вершины $u$ и $v$ лежат в одном и том же дереве поиска в глубину на третьем шаге алгоритма. Значит, они обе достижимы из корня $r$ этого дерева. + +Вершина $r$ была рассмотрена на 3 шаге раньше всех, значит время выхода из неё на 1 шаге больше, чем время выхода из вершин $u$ и $v$. Из этого мы получаем 2 случая: + +Обе эти вершины были достижимы из $r$ в исходном графе. Это означает сильную связность вершин $u$ и $r$ и сильную связность вершин $v$ и $r$ (по Лемме 3). Склеивая пути мы получаем связность вершин $u$ и $v$ (по Лемме 1) + +Хотя бы одна вершина не достижима из $r$ в исходном графе, например $v$. Значит и $r$ была не достижима из $v$ в исходном графе, так как время выхода из $r$ — больше (если бы она была достижима, то время выхода из $v$ было бы больше, чем из $r$, просмотрите ещё раз первый шаг примера). Значит между этими вершинами нет пути (ни в исходном, ни в инвертированном графах), но последнего быть не может, потому что по условию $v$ достижима из r на 3 шаге (в инвертированном графе) + +Значит, из случая 1 и не существования случая 2 получаем, что вершины $u$ и $v$ сильно связаны в обоих графах + +#### Алгоритм Тарьяна без доказательства корректности + +Основой для алгоритма является структура данных "Система непересекающихся множеств", которая и была изобретена Тарьяном (Tarjan). + +Алгоритм фактически представляет собой обход в глубину из корня дерева, в процессе которого постепенно находятся ответы на запросы. А именно, ответ на запрос $(v, u)$ находится, когда обход в глубину находится в вершине $u$, а вершина $v$ уже была посещена, или наоборот. + +Итак, пусть обход в глубину находится в вершине $v$ (и уже были выполнены переходы в её сыновей), и оказалось, что для какого-то запроса $(v, u)$ вершина u уже была посещена обходом в глубину. Научимся тогда находить $LCA$ этих двух вершин. + +Заметим, что $LCA(v, u)$ является либо самой вершиной $v$, либо одним из её предков. Получается, нам надо найти самую нижнюю вершину среди предков $v$ (включая её саму), для которой вершина u является потомком. Заметим, что при фиксированном $v$ по такому признаку (т.е. какой наименьший предок $v$ является и предком какой-то вершины) вершины дерева дерева распадаются на совокупность непересекающихся классов. Для каждого предка $p \neq v$ вершины $v$ её класс содержит саму эту вершину, а также все поддеревья с корнями в тех её сыновьях, которые лежат "слева" от пути до $v$ (т.е. которые были обработаны ранее, чем была достигнута $v$). + +Нам надо научиться эффективно поддерживать все эти классы, для чего мы и применим структуру данных "Система непересекающихся множеств". Каждому классу будет соответствовать в этой структуре множество, причём для представителя этого множества мы определим величину **ANCESTOR** — ту вершину $p$, которая и образует этот класс. + +Рассмотрим подробно реализацию обхода в глубину. Пусть мы стоим в некоторой вершине $v$. Поместим её в отдельный класс в структуре непересекающихся множеств, **ANCESTOR**[v] = $v$. Как обычно в обходе в глубину, перебираем все исходящие рёбра $(v, $to$ )$. Для каждого такого $to$ мы сначала должны вызвать обход в глубину из этой вершины, а потом добавить эту вершину со всем её поддеревом в класс вершины $v$. Это реализуется операцией **Union** структуры данных "система непересекающихся множеств", с последующей установкой **ANCESTOR** = $v$ для представителя множества (т.к. после объединения представитель класса мог измениться). Наконец, после обработки всех рёбер мы перебираем все запросы вида $(v, u)$, и если u была помечена как посещённая обходом в глубину, то ответом на этот запрос будет вершина $LCA(v,u)$ = **ANCESTOR**[FindSet(u)]. Нетрудно заметить, что для каждого запроса это условие (что одна вершина запроса является текущей, а другая была посещена ранее) выполнится ровно один раз. + +Оценим асимптотику. Она складывается из нескольких частей. Во-первых, это асимптотика обхода в глубину, которая в данном случае составляет $O(n)$. Во-вторых, это операции по объединению множеств, которые в сумме для всех разумных n затрачивают $O(n)$ операций. В-третьих, это для каждого запроса проверка условия (два раза на запрос) и определение результата (один раз на запрос), каждое, опять же, для всех разумных n выполняется за $O(1)$. Итоговая асимптотика получается $O(n + m)$, что означает для достаточно больших $m$ $(n = O(m))$ ответ за $O(1)$ на один запрос + +### 1.5. Компоненты реберной двусвязности. Мосты. Поиск мостов + +#### Компоненты реберной двусвязности + +> **Компонентами рёберной двусвязности** (англ. *costal doubly-linked components*) графа называют его подграфы, множества вершин которых - классы эквивалентности рёберной двусвязности, а множества рёбер - множества ребер из соответствующих классов эквивалентности. + +#### Мосты + +> **Мостом** называется такое ребро, удаление которого делает граф несвязным (или, точнее, увеличивает число компонент связности). + +#### Поиск мостов + +- Алгоритм: + Запустим обход в глубину из произвольной вершины графа; обозначим её через **root**. Заметим следующий факт (который несложно доказать): + + Пусть мы находимся в обходе в глубину, просматривая сейчас все рёбра из вершины $v$. Тогда, если текущее ребро $(v,to)$ таково, что из вершины $to$ и из любого её потомка в дереве обхода в глубину нет обратного ребра в вершину $v$ или какого-либо её предка, то это ребро является мостом. В противном случае оно мостом не является. (В самом деле, мы этим условием проверяем, нет ли другого пути из $v$ в $to$ , кроме как спуск по ребру $(v,to)$ дерева обхода в глубину.) + Теперь осталось научиться проверять этот факт для каждой вершины эффективно. Для этого воспользуемся "временами входа в вершину", вычисляемыми алгоритмом поиска в глубину. + + Итак, пусть $tin[v]$ — это время захода поиска в глубину в вершину $v$. Теперь введём массив $fup[v]$, который и позволит нам отвечать на вышеописанные запросы. Время $fup[v]$ равно минимуму из времени захода в саму вершину $tin[v]$, времён захода в каждую вершину $p$, являющуюся концом некоторого обратного ребра $(v, p)$, а также из всех значений $fup[to]$ для каждой вершины $to$ , являющейся непосредственным сыном $v$ в дереве поиска: + + $fup[v] = \min(tin[v], tin[p], fup(to))$ $(v, p)$ - back edge, $(v, to)$ - tree edge (здесь "back edge" — обратное ребро, "tree edge" — ребро дерева) + + Тогда, из вершины $v$ или её потомка есть обратное ребро в её предка тогда и только тогда, когда найдётся такой сын $to$ , что $fup[to] \leq tin[v]$. (Если $fup[to] = tin[v]$, то это означает, что найдётся обратное ребро, приходящее точно в $v$; если же $fup[to] < tin[v]$, то это означает наличие обратного ребра в какого-либо предка вершины $v$.) + + Таким образом, если для текущего ребра $(v,to)$ (принадлежащего дереву поиска) выполняется $fup[to] > tin[v]$, то это ребро является мостом; в противном случае оно мостом не является. + +### 1.6. Компоненты вершинной двусвязности. Точки сочленения. Поиск точек сочленения + +#### Компоненты вершинной двусвязности + +> **Компонентами вершинной двусвязности** графа, называют его подграфы, множества ребер которых — классы эквивалентности вершинной двусвязности, а множества вершин — множества всевозможных концов ребер из соответствующих классов. + +#### Точки сочленения + +> **Точкой сочленения** называется вершина, при удалении которой связный неориентированный граф становится несвязным. + +#### Поиск точек сочленения + +- Алгоритм: + Запустим обход в глубину из произвольной вершины графа; обозначим её через $root$. Заметим следующий факт (который несложно доказать): + + Пусть мы находимся в обходе в глубину, просматривая сейчас все рёбра из вершины $v \neq root$. Тогда, если текущее ребро $(v, to)$ таково, что из вершины $to$ и из любого её потомка в дереве обхода в глубину нет обратного ребра в какого-либо предка вершины v, то вершина $v$ является точкой сочленения. В противном случае, т.е. если обход в глубину просмотрел все рёбра из вершины $v$, и не нашёл удовлетворяющего вышеописанным условиям ребра, то вершина $v$ не является точкой сочленения. (В самом деле, мы этим условием проверяем, нет ли другого пути из $v$ в $to$ ) + Рассмотрим теперь оставшийся случай: $v = root$. Тогда эта вершина является точкой сочленения тогда и только тогда, когда эта вершина имеет более одного сына в дереве обхода в глубину. (В самом деле, это означает, что, пройдя из $root$ по произвольному ребру, мы не смогли обойти весь граф, откуда сразу следует, что $root$ — точка сочленения). + (Ср. формулировку этого критерия с формулировкой критерия для алгоритма поиска мостов.) + + Теперь осталось научиться проверять этот факт для каждой вершины эффективно. Для этого воспользуемся "временами входа в вершину", вычисляемыми алгоритмом поиска в глубину. + + Итак, пусть $tin[v]$ — это время захода поиска в глубину в вершину v. Теперь введём массив $fup[v]$, который и позволит нам отвечать на вышеописанные запросы. Время $fup[v]$ равно минимуму из времени захода в саму вершину $tin[v]$, времён захода в каждую вершину p, являющуюся концом некоторого обратного ребра $(v, p)$, а также из всех значений $fup[to]$ для каждой вершины $to$ , являющейся непосредственным сыном $v$ в дереве поиска: + + $fup[v] = \min(tin[v], tin[p], fup(to))$ $(v, p)$ - back edge, $(v, to)$ - tree edge (здесь "back edge" — обратное ребро, "tree edge" — ребро дерева) + + Тогда, из вершины $v$ или её потомка есть обратное ребро в её предка тогда и только тогда, когда найдётся такой сын $to$ , что $fup[to] < tin[v]$. + + Таким образом, если для текущего ребра $(v,to)$ (принадлежащего дереву поиска) выполняется $fup[to] \geq tin[v]$, то вершина $v$ является точкой сочленения. Для начальной вершины $v = root$ критерий другой: для этой вершины надо посчитать число непосредственных сыновей в дереве обхода в глубину. + +### 1.7. Волновой алгоритм. Обход в ширину (применение очереди в волновом алгоритме) + +#### Волновой алгоритм + +> **Алгоритм волновой трассировки** (*волновой алгоритм*) — алгоритм поиска пути, алгоритм поиска кратчайшего пути на планарном графе. Принадлежит к алгоритмам, основанным на методах поиска в ширину. + +Алгоритм предназначен для поиска кратчайшего пути от стартовой ячейки к конечной ячейке, если это возможно, либо, при отсутствии пути, выдать сообщение о непроходимости. + +Работа алгоритма включает в себя три этапа: инициализацию, распространение волны и восстановление пути. + +Инициализация + +```c +Пометить стартовую ячейку +d := 0 +``` + +Распространение волны + +```c +ЦИКЛ + ДЛЯ каждой ячейки loc, помеченной числом d + пометить все соседние свободные непомеченные ячейки числом d + 1 + КЦ + d := d + 1 +ПОКА (финишная ячейка не помечена) И (есть возможность распространения волны) +``` + +Восстановление пути + +```c +ЕСЛИ финишная ячейка помечена +ТО + перейти в финишную ячейку + ЦИКЛ + выбрать среди соседних ячейку, помеченную числом на 1 меньше числа в текущей ячейке + перейти в выбранную ячейку и добавить её к пути + ПОКА текущая ячейка — не стартовая + ВОЗВРАТ путь найден +ИНАЧЕ + ВОЗВРАТ путь не найден +``` + +#### Обход в ширину (применение очереди в волновом алгоритме) + +> **Поиск в ширину** (англ. *breadth-first search*) — один из основных алгоритмов на графах, позволяющий находить все кратчайшие пути от заданной вершины и решать многие другие задачи. + +- Описание алгоритма + На вход алгоритма подаётся невзвешенный граф и номер стартовой вершины $s$. Граф может быть как ориентированным, так и неориентированным — для алгоритма это не важно. + + Основную идею алгоритма можно понимать как процесс «поджигания» графа: на нулевом шаге мы поджигаем вершину $s$, а на каждом следующем шаге огонь с каждой уже горящей вершины перекидывается на всех её соседей, в конечном счете поджигая весь граф. + + Если моделировать этот процесс, то за каждую итерацию алгоритма будет происходить расширение «кольца огня» в ширину на единицу. Номер шага, на котором вершина $v$ начинает гореть, в точности равен длине её минимального пути из вершины s. + +```c++ +std::vector graph; + +void bfs(int s) { + std::queue queue; + queue.push(s); + + std::vector depth(n, -1), parent(n); + depth[s] = 0; + + while (!queue.empty()) { + int vertex = queue.front(); + queue.pop(); + for (int u : g[vertex]) { + if (depth[u] == -1) { + queue.push(u); + depth[u] = depth[vertex] + 1; + parent[u] = vertex; + } + } + } +} +``` + +Теперь, чтобы восстановить кратчайший путь до какой-то вершины v, это можно сделать через массив p: + +```c++ +while (v != s) { + std::cout << v << endl; + v = p[v]; +} +``` + +Обратим внимание, что путь выведется в обратном порядке. + +### 1.8. Критерий существования Эйлерова пути и цикла в ориентированном и неориентированном графе. Поиск эйлерова пути и цикла + +#### Критерий существования Эйлерова пути и цикла в неориентированном графе + +- Согласно теореме, доказанной Эйлером, эйлеров цикл существует тогда и только тогда, когда + 1. Граф связный или будет являться связным, если удалить из него все изолированные вершины. + 2. В нём отсутствуют вершины нечётной степени. + +- Эйлеров путь в графе существует тогда и только тогда, когда + 1. Граф связный + 2. Содержит не более двух вершин нечётной степени. Ввиду леммы о рукопожатиях, число вершин с нечётной степенью должно быть чётным. А значит эйлеров путь существует только тогда, когда это число равно нулю или двум. Причём, когда оно равно нулю, эйлеров путь вырождается в эйлеров цикл. + +#### Критерий существования Эйлерова пути и цикла в ориентированном графе + +- В ориентированном графе $G = (V, E)$ существует эйлеров цикл тогда и только тогда, когда: + 1. Входная степень любой вершины равна ее выходной степени. + 2. Все компоненты слабой связности кроме, может быть одной, не содержат ребер. +- В ориентированном графе $G = (V, E)$ существует эйлеров путь если: + 1. Входная степень любой вершины равна ее выходной степени, кроме двух вершин графа, для одной из которых (deg+) − (deg−) = 1, а для другой (deg+) − (deg−) = −1 . + 2. Все компоненты слабой связности кроме, может быть одной, не содержат ребер. + +#### Поиск эйлерова пути и цикла + +- Поиск эйлерова пути в графе + Можно всегда свести задачу поиска эйлерова пути к задаче поиска эйлерова цикла. Действительно, предположим, что эйлерова цикла не существует, а эйлеров путь существует. Тогда в графе будет ровно 2 вершины нечётной степени. Соединим эти вершины ребром, и получим граф, в котором все вершины чётной степени, и эйлеров цикл в нём существует. Найдём в этом графе эйлеров цикл (алгоритмом, описанным ниже), а затем удалим из ответа несуществующее ребро. + +- Поиск эйлерова цикла в графе + Будем рассматривать самый общий случай — случай ориентированного мультиграфа, возможно, с петлями. Также мы предполагаем, что эйлеров цикл в графе существует (и состоит хотя бы из одной вершины). Для поиска эйлерова цикла воспользуемся тем, что эйлеров цикл — это объединение всех простых циклов графа. Следовательно, наша задача — эффективно найти все циклы и эффективно объединить их в один. + + Реализовать это можно, например, так, рекурсивно: + + ```c + procedure find_all_cycles (v) + var массив cycles + 1. пока есть цикл, проходящий через v, находим его + добавляем все вершины найденного цикла в массив cycles (сохраняя порядок обхода) + удаляем цикл из графа + 2. идем по элементам массива cycles + каждый элемент cycles[i] добавляем к ответу + из каждого элемента рекурсивно вызываем себя: find_all_cycles (cycles[i]) + ``` + + Достаточно вызвать эту процедуру из любой вершины графа, и она найдёт все циклы в графе, удалит их из графа и объединит их в один эйлеров цикл. + Для поиска цикла на шаге 1 используем поиск в глубину. + Сложность полученного алгоритма — $O(|E|)$, то есть линейная относительно количества рёбер в данном графе + \ No newline at end of file From 222b6e27e369d07f30a9d5cbf1119f53c86abde3 Mon Sep 17 00:00:00 2001 From: Egor Ivanov <56685477+sibenshtern@users.noreply.github.com> Date: Sun, 7 Jan 2024 04:02:31 +0000 Subject: [PATCH 2/7] Finish second part of theory --- doc/Theory.md | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 68 insertions(+), 1 deletion(-) diff --git a/doc/Theory.md b/doc/Theory.md index 820f927..869d316 100644 --- a/doc/Theory.md +++ b/doc/Theory.md @@ -34,6 +34,15 @@ - [Критерий существования Эйлерова пути и цикла в неориентированном графе](#критерий-существования-эйлерова-пути-и-цикла-в-неориентированном-графе) - [Критерий существования Эйлерова пути и цикла в ориентированном графе](#критерий-существования-эйлерова-пути-и-цикла-в-ориентированном-графе) - [Поиск эйлерова пути и цикла](#поиск-эйлерова-пути-и-цикла) + - [2. Планарность графа](#2-планарность-графа) + - [2.1. Формула Эйлера. Теорема Портнягина-Куратовского. Теорема Вагнера](#21-формула-эйлера-теорема-портнягина-куратовского-теорема-вагнера) + - [Формула Эйлера](#формула-эйлера) + - [Теорема Портнягина-Куратовского](#теорема-портнягина-куратовского) + - [Теорема Вагнера](#теорема-вагнера) + - [2.2. Гамма алгоритм. Контактная вершина](#22-гамма-алгоритм-контактная-вершина) + - [Гамма алгоритм](#гамма-алгоритм) + - [2.3. Теорема о корректности Гамма алгоритма. Асимптотика алгоритма](#23-теорема-о-корректности-гамма-алгоритма-асимптотика-алгоритма) + - [Теорема о корректности Гамма алгоритма](#теорема-о-корректности-гамма-алгоритма) ## 1. Обходы графа @@ -465,4 +474,62 @@ while (v != s) { Достаточно вызвать эту процедуру из любой вершины графа, и она найдёт все циклы в графе, удалит их из графа и объединит их в один эйлеров цикл. Для поиска цикла на шаге 1 используем поиск в глубину. Сложность полученного алгоритма — $O(|E|)$, то есть линейная относительно количества рёбер в данном графе - \ No newline at end of file + +## 2. Планарность графа + +### 2.1. Формула Эйлера. Теорема Портнягина-Куратовского. Теорема Вагнера + +#### Формула Эйлера + +Для произвольного плоского связного графа $G$ с $V$ вершинами, $E$ ребрами и $F$ гранями справедливо следующее соотношение: $V − E + F = 2$. + +#### Теорема Портнягина-Куратовского + +Теорема Понтрягина — Куратовского, или теорема Куратовского, — теорема в теории графов, дающая необходимое и достаточное условие планарности графа. Имеет эквивалентную формулировку на языке миноров, так называемою теорему Вагнера. + +Теорема утверждает, что графы $K_5$ (полный граф на 5 вершинах) и $K_{3,3}$ (полный двудольный граф имеющий по 3 вершины в каждой доле) являются единственными минимальными непланарными графами. + +#### Теорема Вагнера + +Теорема Вагнера — характеризация планарных графов тесно связанная с теоремой Понтрягина — Куратовского. + +Теорема утверждает, что конечный граф является планарным тогда и только тогда, когда его миноры не включают ни $K_5$ (полный граф с пятью вершинами), ни $K_{3,3}$ (коммунальный граф, полный двудольный граф с тремя вершинами в каждой доле). + +### 2.2. Гамма алгоритм. Контактная вершина + +#### Гамма алгоритм + +- Чтобы проверить планарность графа и произвести его плоскую укладку, удобно пользоваться гамма-алгоритмом. +- Определения: + 1. Сегмент графа $Г$ относительно подграфа $H$ — компонента связности графа $G \setminus H$. + 2. *Допустимая грань сегмента* — грань графа $H$, содержащая все контактные вершины. + 3. **Контактная вершина** сегмента $S$ графа $G$ подграфа $H$ — любая вершина в $S$ и в $H$. + 4. *Гамма-цепь сегмента* — любая цепь без повторов вершин, содержащих ровно две контактные вершины — начало и конец. +- Алгоритм: + 1. Уложить на плоскость любой цикл $H$ графа $G$ без повторов вершин. + 2. Построить все сегменты $S_1$ , $...$ , $S_k$ графа $G$ по $H$. + 3. Если есть сегмент $S_i$ c одной допустимой гранью — выбрать его. + 4. Если все сегменты имеют несколько дополнительных граней — выбрать любой. + 5. Выбрать произвольную гамма-цепь сегмента и уложить её в допустимую грань. + 6. Перейти к шагу (2), добавив гамма-цепь к графу $H$. + +### 2.3. Теорема о корректности Гамма алгоритма. Асимптотика алгоритма + +#### Теорема о корректности Гамма алгоритма + +- Теорема: Гамма-алгоритм корректен, то есть если $G$ — планарный граф, то результатом каждого шага гамма-алгоритма является частичная укладка $G'$. +- Доказательство: + Докажем индукцией по числу шагов. + + База индукции: полученный на этапе инициализации граф $G_0$ является простым циклом, он будет присутствовать в любой укладке графа $G$. Таким образом, $G_0$ является частичной укладкой. + + Шаг индукции: пусть граф $G_{k−1}$, полученный на $k−1$-м шаге работы алгоритма, является частичной укладкой. Докажем, что граф $G_k = G_{k−1} \cup L_k$, полученный на $k$-м шаге присоединением цепи $L_k$, также является частичной укладкой. + + Заметим, что на текущем шаге нет такого сегмента $S$ относительно $G_{k−1}$, для которого бы выполнялось равенство $Γ(S) = \varnothing$, так как в противном случае существовала бы цепь этого сегмента, контактные вершины которой принадлежали бы разным граням и укладка которой была бы невозможна. Следовательно, нельзя было бы уложить $S$,что противоречит тому, что $G$ — планарный граф. Значит, мы можем рассматривать только следующие два случая: + 1. Существует сегмент $S$ для которого есть единственная вмещающая грань $Γ$, то есть $|Γ(S)| = 1$. Так как только грани $Γ$ принадлежат все контактные вершины $S$, то укладка этого сегмента в эту грань неизбежна. Это значит, что помещая любую цепь $L \subset S$, снова получим частичную укладку графа. + + 2. Для любого сегмента $S$ $|Γ(S)| \geq 2$. Построим граф $A(G'k−1)$, который по лемме 2 является двудольным. Рассмотрим его связную компоненту $K$, которая содержит не менее двух вершин. Граф $K$ также является двудольным. По лемме 1 для любого сегмента $S \in K$ справедливо $Γ(S)={Γ1, Γ2}$. Так как граф $K$ двудольный, то мы можем по очереди помещать сегменты $K$ в разные грани, причем конфликтующих сегментов не возникнет в силу четности всех циклов в графе. Результатом будет частичная укладка графа. + + Таким образом, на каждом шаге мы получаем частичную укладку графа, что доказывает корректность гамма-алгоритма. + +**Асимптотика алгоритма - O(n^3)** From a5a28c73616648f6567170178ddffa67ff198225 Mon Sep 17 00:00:00 2001 From: Egor Ivanov <56685477+sibenshtern@users.noreply.github.com> Date: Sun, 7 Jan 2024 04:35:34 +0000 Subject: [PATCH 3/7] finish third part of theory --- doc/Theory.md | 305 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 304 insertions(+), 1 deletion(-) diff --git a/doc/Theory.md b/doc/Theory.md index 869d316..f9ba607 100644 --- a/doc/Theory.md +++ b/doc/Theory.md @@ -43,6 +43,35 @@ - [Гамма алгоритм](#гамма-алгоритм) - [2.3. Теорема о корректности Гамма алгоритма. Асимптотика алгоритма](#23-теорема-о-корректности-гамма-алгоритма-асимптотика-алгоритма) - [Теорема о корректности Гамма алгоритма](#теорема-о-корректности-гамма-алгоритма) + - [3. Кратчайшие пути во взвешенном графе](#3-кратчайшие-пути-во-взвешенном-графе) + - [3.1. Алгоритм Дейкстры. Доказательство корректности(с доказательством). Оценка времени работы. Дерево кратчайших путей](#31-алгоритм-дейкстры-доказательство-корректностис-доказательством-оценка-времени-работы-дерево-кратчайших-путей) + - [Алгоритм Дейкстры](#алгоритм-дейкстры) + - [Доказательство корректности](#доказательство-корректности) + - [Оценка времени работы](#оценка-времени-работы) + - [Дерево кратчайших путей](#дерево-кратчайших-путей) + - [3.2. Потенциалы. Условие применимости алгоритма Дейкстры для измененных длин ребер. Потенциал $\\pi(v) = \\rho(v, t)$](#32-потенциалы-условие-применимости-алгоритма-дейкстры-для-измененных-длин-ребер-потенциал-piv--rhov-t) + - [Потенциалы](#потенциалы) + - [Условие применимости алгоритма Дейкстры для измененных длин ребер](#условие-применимости-алгоритма-дейкстры-для-измененных-длин-ребер) + - [Потенциал $\\pi(v) = \\rho(v, t)$](#потенциал-piv--rhov-t) + - [3.3. Алгоритм A\*. Условие монотонности на эвристику. Примеры эвристик](#33-алгоритм-a-условие-монотонности-на-эвристику-примеры-эвристик) + - [Алгоритм A\*](#алгоритм-a) + - [Примеры эвристик](#примеры-эвристик) + - [3.4. Алгоритм Форда-Беллмана. Хранение в матрице: $D\_{vk}$ равно длине кратчайшего пути до вершины $v$ за ровно $k$ ребер (не более $k$ ребер). Доказательство корректности (полное). Оценка времени работы](#34-алгоритм-форда-беллмана-хранение-в-матрице-d_vk-равно-длине-кратчайшего-пути-до-вершины-v-за-ровно-k-ребер-не-более-k-ребер-доказательство-корректности-полное-оценка-времени-работы) + - [Доказательство корректности(полное)](#доказательство-корректностиполное) + - [Оценка времени работы](#оценка-времени-работы-1) + - [3.5. Восстановление пути. Детектирование цикла отрицательного веса. Поиск самого цикла](#35-восстановление-пути-детектирование-цикла-отрицательного-веса-поиск-самого-цикла) + - [Восстановление пути](#восстановление-пути) + - [Детектирование цикла отрицательного веса](#детектирование-цикла-отрицательного-веса) + - [Поиск самого цикла](#поиск-самого-цикла) + - [3.6. Нахождение кратчайших путей с учетом циклов отрицательного веса](#36-нахождение-кратчайших-путей-с-учетом-циклов-отрицательного-веса) + - [3.7. Алгоритм Флойда. Доказательство (концепция). Восстановление пути](#37-алгоритм-флойда-доказательство-концепция-восстановление-пути) + - [Алгоритм Флойда](#алгоритм-флойда) + - [Доказательство (концепция)](#доказательство-концепция) + - [Восстановление пути](#восстановление-пути-1) + - [3.8. Нахождение цикла отрицательного веса](#38-нахождение-цикла-отрицательного-веса) + - [3.9. Алгоритм Джонсона. Добавление фиктивного корня и фиктивных ребер для запуска алгоритма Форда-Беллмана](#39-алгоритм-джонсона-добавление-фиктивного-корня-и-фиктивных-ребер-для-запуска-алгоритма-форда-беллмана) + - [Алгоритм Джонсона](#алгоритм-джонсона) + - [Добавление фиктивного корня и фиктивных ребер для запуска алгоритма Форда-Беллмана](#добавление-фиктивного-корня-и-фиктивных-ребер-для-запуска-алгоритма-форда-беллмана) ## 1. Обходы графа @@ -532,4 +561,278 @@ while (v != s) { Таким образом, на каждом шаге мы получаем частичную укладку графа, что доказывает корректность гамма-алгоритма. -**Асимптотика алгоритма - O(n^3)** +**Асимптотика алгоритма - $O(n^3)$.** + +## 3. Кратчайшие пути во взвешенном графе + +### 3.1. Алгоритм Дейкстры. Доказательство корректности(с доказательством). Оценка времени работы. Дерево кратчайших путей + +#### Алгоритм Дейкстры + +**Алгоритм Дейкстры** (англ. *Dijkstra’s algorithm*) — алгоритм на графах, изобретённый нидерландским учёным Эдсгером Дейкстрой в 1959 году. Находит кратчайшие пути от одной из вершин графа до всех остальных. Алгоритм работает только для графов без рёбер отрицательного веса. + +```c +func dijkstra(s): + for $v \in V$ + d[v] = $ \infinity $ + used[v] = false + d[s] = 0 + for $i \in V$ + v = null + for $j \in V$ // найдём вершину с минимальным расстоянием + if !used[j] and (v == null or d[j] < d[v]) + v = j + if d[v] == $\infinity$ + break + used[v] = true + for e : исходящие из v рёбра // произведём релаксацию по всем рёбрам, исходящим из v + if d[v] + e.len < d[e.to] + d[e.to] = d[v] + e.len +``` + +#### Доказательство корректности + +- Пусть $G=(V,E)$ — ориентированный взвешенный граф, вес рёбер которого неотрицателен, s — стартовая вершина. Тогда после выполнения алгоритма Дейкстры $d(u)=\rho(s, u)$ для всех $u$, где $\rho (s,u)$ — длина кратчайшего пути из вершины $s$ в вершину $u$. + +Докажем по индукции, что в момент посещения любой вершины $u$, $d(u)= \rho (s,u)$. + +На первом шаге выбирается $s$, для неё выполнено: $d(s) = \rho (s,s) = 0$. Пусть для $n$ первых шагов алгоритм сработал верно и на $n+1$ шагу выбрана вершина $u$. Докажем, что в этот момент $d(u)= \rho (s,u)$. Для начала отметим, что для любой вершины $v$, всегда выполняется $d(v) \geq \rho (s,v)$ (алгоритм не может найти путь короче, чем кратчайший из всех существующих). Пусть $P$ — кратчайший путь из $s$ в $u$, $v$ — первая непосещённая вершина на $P$, $z$ — предшествующая ей (следовательно, посещённая). Поскольку путь $P$ кратчайший, его часть, ведущая из $s$ через $z$ в $v$, тоже кратчайшая, следовательно $\rho(s,v)= \rho(s,z)+w(zv)$. По предположению индукции, в момент посещения вершины $z$ выполнялось $d(z)= \rho (s,z)$, следовательно, вершина $v$ тогда получила метку не больше чем $d(z) + w(zv) = \rho (s,z) + w(zv) = \rho(s,v)$, следовательно, $d(v)=\rho(s,v)$. С другой стороны, поскольку сейчас мы выбрали вершину $u$, её метка минимальна среди непосещённых, то есть $d(u) \leq d(v) = \rho (s,v) \leq \rho (s,u)$, где второе неравенсто верно из-за ранее упомянутого определения вершины $v$ в качестве первой непосещённой вершины на $P$, то есть вес пути до промежуточной вершины не превосходит веса пути до конечной вершины вследствие неотрицательности весовой функции. Комбинируя это с $d(u) \geq \rho(s,u)$, имеем $d(u)=\rho(s,u)$, что и требовалось доказать. Поскольку алгоритм заканчивает работу, когда все вершины посещены, в этот момент $d(u)= \rho (s,u)$ для всех $u$. + +#### Оценка времени работы + +- Если $m = n ^ 2$(граф плотный), то $O(n^2)$. +- Если $m = n$(граф разреженный), то $O(m log n)$. + +#### Дерево кратчайших путей + +Решение ряда прикладных задач сводится к нахождению дерева кратчайших путей с корнем в заданной вершине $s$. Такое дерево дает кратчайшие $(s, v)$-пути от заданной вершины $s$ до любой вершины $v$ орграфа $G = (V, Е)$. Если веса всех дуг орграфа $G$ неотрицательны, то дерево кратчайших путей можно построить с помощью алгоритма Дейкстры. + +### 3.2. Потенциалы. Условие применимости алгоритма Дейкстры для измененных длин ребер. Потенциал $\pi(v) = \rho(v, t)$ + +#### Потенциалы + +Пусть дана транспортная сеть $G(V,E)$, где $V$ — множество вершин графа, а $E$ — множество рёбер. Введем в каждой вершине потенциал $\pi(v)$. Тогда потенциальный вес (то есть стоимость) ребра $(u,v)$ определяется как $w_p(u,v)=w(u,v)+\pi(u)−\pi(v)$ + +#### Условие применимости алгоритма Дейкстры для измененных длин ребер + +Можно применять, когда нет отрицательных циклов (следует из курса дискретной математики 1 курс 2 семестр) + +#### Потенциал $\pi(v) = \rho(v, t)$ + +### 3.3. Алгоритм A*. Условие монотонности на эвристику. Примеры эвристик + +#### Алгоритм A* + +Алгоритм A* - алгоритм поиска по первому наилучшему совпадению на графе, который находит маршрут с наименьшей стоимостью от одной вершины (начальной) к другой (целевой, конечной). + +Порядок обхода вершин определяется эвристической функцией «расстояние + стоимость» (обычно обозначаемой как $f(x)$). Эта функция — сумма двух других: функции стоимости достижения рассматриваемой вершины (x) из начальной (обычно обозначается как $g(x)$ и может быть как эвристической, так и нет), и функции эвристической оценки расстояния от рассматриваемой вершины к конечной (обозначается как $h(x)$). + +1. Функция $h(x)$ должна быть *допустимой эвристической оценкой*, то есть не должна переоценивать расстояния к целевой вершине. Например, для задачи маршрутизации $h(x)$ может представлять собой расстояние до цели по прямой линии, так как это физически наименьшее возможное расстояние между двумя точками. +2. Более сильное условие — функция $h(v)$ должна быть **монотонной**. (Эвристическая функция $h(v)$ называетя **монотонной** (или преемственной), если для любой вершины $v_1$ и ее потомка $v_2$ разность $h(v_1)$ и $h(v_2)$ не превышает фактического веса ребра $c(v_1, v_2)$ от $v_1$ до $v_2$, а эвристическая оценка целевого состояния равна нулю.) + +```c +function A*(start, goal, f) + % множество уже пройденных вершин + var closed := the empty set + % множество частных решений + var open := make_queue(f) + enqueue(open, path(start)) + while open is not empty + var p := remove_first(open) + var x := the last node of p + if x in closed + continue + if x = goal + return $\rho$ + add(closed, x) + % добавляем смежные вершины + foreach y in successors(x) + enqueue(open, add_to_path(p, y)) + return failure +``` + +#### Примеры эвристик + +- Если мы можем перемещаться в четырех направлениях, то в качестве эвристики стоит выбрать манхэттенское расстояние $h(v)=|v.x−goal.x|+|v.y−goal.y|$. +- Расстояние Чебышева применяется, когда к четырем направлениям добавляются диагонали: $h(v)=max(|v.x−goal.x|,|v.y−goal.y|)$. +- Если передвижение не ограничено сеткой, то можно использовать евклидово расстояние по прямой: $h(v)=((v.x−goal.x) ^ 2+(v.y−goal.y)^2) ^ {1/2}$. + +### 3.4. Алгоритм Форда-Беллмана. Хранение в матрице: $D_{vk}$ равно длине кратчайшего пути до вершины $v$ за ровно $k$ ребер (не более $k$ ребер). Доказательство корректности (полное). Оценка времени работы + +Количество путей длины k рёбер можно найти с помощью метода динамического программирования. Пусть $d[k][u]$ — количество путей длины k рёбер, заканчивающихся в вершине u. Тогда $d[k][u]= \Sigma v: v u \in E(d[k−1][v])$. + +Аналогично посчитаем пути кратчайшей длины. Пусть $s$ — стартовая вершина. Тогда $d[k][u]=\min v: v u \in E(d[k−1][v]+w(u,v))$, при этом $d[0][s]=0$, а $d[0][u]=+\infty$ + +Если существует кратчайший путь от s до t, то $\rho(s,t)=\min d[k][t](k=0..n−1)$ + +Пусть кратчайший путь состоит из $k$ ребер, тогда корректность формулы следует из динамики, приведенной ниже. + +```c++ +for k = 0 to |V|−2 // вершины нумеруются с единицы + for v∈V + for (u,v)∈E + d[k + 1][v] = min(d[k + 1][v], d[k][u] + ω(u,v)) // ω(u,v) — вес ребра uv +``` + + +```c++ +bool fordBellman(s): + for v∈V + d[v] = 1 + d[s] = 0 + for i = 0 to |V|−1 + for (u,v)∈E + if d[v] > d[u] + ω(u,v) // ω(u,v) — вес ребра uv + d[v] = d[u] + ω(u,v) + for (u,v)∈E + if d[v] > d[u] + ω(u,v) + return false + return true +``` + +#### Доказательство корректности(полное) + +Во-первых, сразу заметим, что для недостижимых из $v$ вершин алгоритм отработает корректно: для них метка $d[]$ так и останется равной бесконечности (т.к. алгоритм Форда-Беллмана найдёт какие-то пути до всех достижимых из $s$ вершин, а релаксация во всех остальных вершинах не произойдёт ни разу). + +Докажем теперь следующее утверждение: после выполнения $i$ фаз алгоритм Форда-Беллмана корректно находит все кратчайшие пути, длина которых (по числу рёбер) не превосходит $i$. + +Иными словами, для любой вершины $a$ обозначим через $k$ число рёбер в кратчайшем пути до неё (если таких путей несколько, можно взять любой). Тогда это утверждение говорит о том, что после $k$ фаз этот кратчайший путь будет найден гарантированно. + +**Доказательство.** + +Рассмотрим произвольную вершину a, до которой существует путь из стартовой вершины v, и рассмотрим кратчайший путь до неё: $(p_0=v, p_1, ..., p_k=a)$. Перед первой фазой кратчайший путь до вершины $p_0=v$ найден корректно. Во время первой фазы ребро $(p_0,p_1)$ было просмотрено алгоритмом Форда-Беллмана, следовательно, расстояние до вершины $p_1$ было корректно посчитано после первой фазы. Повторяя эти утверждения k раз, получаем, что после $k$-й фазы расстояние до вершины $p_k=a$ посчитано корректно, что и требовалось доказать. + +Последнее, что надо заметить — это то, что любой кратчайший путь не может иметь более $n-1$ ребра. Следовательно, алгоритму достаточно произвести только $n-1$ фазу. После этого ни одна релаксация гарантированно не может завершиться улучшением расстояния до какой-то вершины. + +#### Оценка времени работы + +Инициализация занимает $\theta(V)$ времени, каждый из $|V|−1$ проходов требует $\theta(E)$ времени, обход по всем ребрам для проверки наличия отрицательного цикла занимает $O(E)$ времени. Значит алгоритм Беллмана-Форда работает за $O(VE)$ времени. + +### 3.5. Восстановление пути. Детектирование цикла отрицательного веса. Поиск самого цикла + +#### Восстановление пути + +```c++ +void solve() { + vector d (n, INF); + d[v] = 0; + vector p (n, -1); + for (;;) { + bool any = false; + for (int j=0; j d[e[j].a] + e[j].cost) { + d[e[j].b] = d[e[j].a] + e[j].cost; + p[e[j].b] = e[j].a; + any = true; + } + if (!any) break; + } + + if (d[t] == INF) + cout << "No path from " << v << " to " << t << "."; + else { + vector path; + for (int cur=t; cur!=-1; cur=p[cur]) + path.push_back (cur); + reverse (path.begin(), path.end()); + + cout << "Path from " << v << " to " << t << ": "; + for (size_t i=0; i **Алгоритм Флойда** (*алгоритм Флойда–Уоршелла*) — алгоритм нахождения длин кратчайших путей между всеми парами вершин во взвешенном ориентированном графе. Работает корректно, если в графе нет циклов отрицательной величины, а в случае, когда такой цикл есть, позволяет найти хотя бы один такой цикл. + +Ключевая идея алгоритма — разбиение процесса поиска кратчайших путей на фазы. + +Перед $k$-ой фазой $(k = 1 ... n)$ считается, что в матрице расстояний $d[][]$ сохранены длины таких кратчайших путей, которые содержат в качестве внутренних вершин только вершины из множества ${1, 2, ..., k-1}$ (вершины графа мы нумеруем, начиная с единицы). + +Иными словами, перед $k$-ой фазой величина $d[i][j]$ равна длине кратчайшего пути из вершины $i$ в вершину $j$, если этому пути разрешается заходить только в вершины с номерами, меньшими k (начало и конец пути не считаются). + +Легко убедиться, что чтобы это свойство выполнилось для первой фазы, достаточно в матрицу расстояний $d[][]$ записать матрицу смежности графа: $d[i][j] = g[i][j]$ — стоимости ребра из вершины $i$ в вершину $j$. При этом, если между какими-то вершинами ребра нет, то записать следует величину "бесконечность". Из вершины в саму себя всегда следует записывать величину 0, это критично для алгоритма. + +Пусть теперь мы находимся на $k$-ой фазе, и хотим пересчитать матрицу $d[][]$ таким образом, чтобы она соответствовала требованиям уже для $k+1$-ой фазы. Зафиксируем какие-то вершины $i$ и $j$. У нас возникает два принципиально разных случая: + +Кратчайший путь из вершины $i$ в вершину $j$, которому разрешено дополнительно проходить через вершины ${1, 2, ..., k}$, совпадает с кратчайшим путём, которому разрешено проходить через вершины множества ${1, 2, ..., k-1}$. +В этом случае величина $d[i][j]$ не изменится при переходе с $k$-ой на $k+1$-ую фазу. + +"Новый" кратчайший путь стал лучше "старого" пути. Это означает, что "новый" кратчайший путь проходит через вершину $k$. Сразу отметим, что мы не потеряем общности, рассматривая далее только простые пути (т.е. пути, не проходящие по какой-то вершине дважды). + +Тогда заметим, что если мы разобьём этот "новый" путь вершиной $k$ на две половинки (одна идущая $i \rightarrow k$, а другая — $k \rightarrow j$), то каждая из этих половинок уже не заходит в вершину $k$. Но тогда получается, что длина каждой из этих половинок была посчитана ещё на $k-1$-ой фазе или ещё раньше, и нам достаточно взять просто сумму $d[i][k] + d[k][j]$, она и даст длину "нового" кратчайшего пути. + +Объединяя эти два случая, получаем, что на $k$-ой фазе требуется пересчитать длины кратчайших путей между всеми парами вершин $i$ и $j$ следующим образом: + +```c++ +new_d[i][j] = min(d[i][j], d[i][k] + d[k][j]); +``` + +Таким образом, вся работа, которую требуется произвести на $k$-ой фазе — это перебрать все пары вершин и пересчитать длину кратчайшего пути между ними. В результате после выполнения $n$-ой фазы в матрице расстояний $d[i][j]$ будет записана длина кратчайшего пути между $i$ и $j$, либо бесконечность, если пути между этими вершинами не существует. + +Последнее замечание, которое следует сделать, — то, что можно не создавать отдельную матрицу $new_d[][]$ для временной матрицы кратчайших путей на $k$-ой фазе: все изменения можно делать сразу в матрице $d[][]$. В самом деле, если мы улучшили (уменьшили) какое-то значение в матрице расстояний, мы не могли ухудшить тем самым длину кратчайшего пути для каких-то других пар вершин, обработанных позднее. + +#### Доказательство (концепция) + +Доказательство "правильности" работы этого алгоритма также очевидно и выполняется с помощью математической индукции по $k$, показывая, что на $k$-й итерации вершина $k$ включается в путь только тогда, когда новый путь короче старого. + +#### Восстановление пути + +Легко поддерживать дополнительную информацию — так называемых "предков", по которым можно будет восстанавливать сам кратчайший путь между любыми двумя заданными вершинами в виде последовательности вершин. + +Для этого достаточно кроме матрицы расстояний $d[][]$ поддерживать также матрицу предков $p[][]$, которая для каждой пары вершин будет содержать номер фазы, на которой было получено кратчайшее расстояние между ними. Понятно, что этот номер фазы является не чем иным, как "средней" вершиной искомого кратчайшего пути, и теперь нам просто надо найти кратчайший путь между вершинами $i$ и $p[i][j]$, а также между $p[i][j]$ и $j$. Отсюда получается простой рекурсивный алгоритм восстановления кратчайшего пути. + +### 3.8. Нахождение цикла отрицательного веса + +Если в графе есть циклы отрицательного веса, то формально алгоритм Флойда-Уоршелла неприменим к такому графу. + +На самом же деле, для тех пар вершин $i$ и $j$, между которыми нельзя зайти в цикл отрицательного вес, алгоритм отработает корректно. + +Для тех же пар вершин, ответа для которых не существует (по причине наличия отрицательного цикла на пути между ними), алгоритм Флойда найдёт в качестве ответа какое-то число (возможно, сильно отрицательное, но не обязательно). Тем не менее, можно улучшить алгоритм Флойда, чтобы он аккуратно обрабатывал такие пары вершин и выводил для них, например, - бесконечность. + +Для этого можно сделать, например, следующий критерий "не существования пути". Итак, пусть на данном графе отработал обычный алгоритм Флойда. Тогда между вершинами $i$ и $j$ не существует кратчайшего пути тогда и только тогда, когда найдётся такая вершина $t$, достижимая из $i$ и из которой достижима $j$, для которой выполняется $d[t][t] < 0$. + +Кроме того, при использовании алгоритма Флойда для графов с отрицательными циклами следует помнить, что возникающие в процессе работы расстояния могут сильно уходить в минус, экспоненциально с каждой фазой. Поэтому следует принять меры против целочисленного переполнения, ограничив все расстояния снизу какой-нибудь величиной (например, - Бесконечность). + +### 3.9. Алгоритм Джонсона. Добавление фиктивного корня и фиктивных ребер для запуска алгоритма Форда-Беллмана + +#### Алгоритм Джонсона + +> Алгоритм Джонсона позволяет найти кратчайшие пути между всеми парами вершин в течение времени $O(V \cdot 2 \log(V) + VE)$. Для разреженных графов этот алгоритм ведет себя асимптотически быстрее алгоритма Флойда. Этот алгоритм либо возвращает матрицу кратчайших расстояний между всеми парами вершин, либо сообщение о том, что в графе существует цикл отрицательной длины. + +В этом алгоритме используется метод изменения веса (англ. reweighting). Суть его заключается в том, что для заданного графа G строится новая весовая функция $\omega_\varphi$, неотрицательная для всех ребер графа G и сохраняющая кратчайшие пути. Такая весовая функция строится с помощью так называемой потенциальной функции. + +Пусть $\varphi: V \rightarrow R$ — произвольное отображение из множества вершин в вещественные числа. Тогда новой весовой функцией будет $\omega_\varphi(u,v)=\varphi(u,v)+\varphi(u)−\varphi(v)$. + +Такая потенциальная функция строится добавлем фиктивной вершины $s$ в $G$, из которой проведены ориентированные ребра нулевого веса во все остальные вершины графа, и запуском алгоритма Форда-Беллмана из нее ($\varphi(v)$ будет равно длине кратчайшего пути из $s$ в $v$). На этом же этапе мы сможем обнаружить наличие отрицательного цикла в графе. + +Теперь, когда мы знаем, что веса всех ребер неотрицательны, и кратчайшие пути сохранятся, можно запустить алгоритм Дейкстры из каждой вершины и таким образом найти кратчайшие расстояния между всеми парами вершин. + +#### Добавление фиктивного корня и фиктивных ребер для запуска алгоритма Форда-Беллмана + +Как в случае применения алгоритма Беллмана-Форда для обнаружения отрицательных циклов, существуют два способа сделать вес каждого ребра неотрицательным в произвольной сети без отрицательных циклов. Либо можно начать с истока в каждом сильно связном компоненте, либо добавить фиктивную вершину с ребром нулевой длины в каждую вершину сети. В любом случае получится остовный лес кратчайших путей, которым можно воспользоваться для присвоения весов вершинам (вес пути из корня в данную вершину в ее SPT). From c50cad092ccd7f3bd479e7f9839488a94f84ded7 Mon Sep 17 00:00:00 2001 From: Egor Ivanov <56685477+sibenshtern@users.noreply.github.com> Date: Sun, 7 Jan 2024 05:20:31 +0000 Subject: [PATCH 4/7] finish theory --- doc/Theory.md | 503 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 503 insertions(+) diff --git a/doc/Theory.md b/doc/Theory.md index f9ba607..cd5202a 100644 --- a/doc/Theory.md +++ b/doc/Theory.md @@ -72,6 +72,68 @@ - [3.9. Алгоритм Джонсона. Добавление фиктивного корня и фиктивных ребер для запуска алгоритма Форда-Беллмана](#39-алгоритм-джонсона-добавление-фиктивного-корня-и-фиктивных-ребер-для-запуска-алгоритма-форда-беллмана) - [Алгоритм Джонсона](#алгоритм-джонсона) - [Добавление фиктивного корня и фиктивных ребер для запуска алгоритма Форда-Беллмана](#добавление-фиктивного-корня-и-фиктивных-ребер-для-запуска-алгоритма-форда-беллмана) + - [4. Остовные деревья](#4-остовные-деревья) + - [4.1. Остовное дерево. Построение с помощью обхода в глубину и в ширину](#41-остовное-дерево-построение-с-помощью-обхода-в-глубину-и-в-ширину) + - [Остовное дерево](#остовное-дерево) + - [Построение с помощью обхода в глубину и в ширину](#построение-с-помощью-обхода-в-глубину-и-в-ширину) + - [4.2. Определение минимального остовного дерева](#42-определение-минимального-остовного-дерева) + - [4.3. Теорема о разрезе. Доказательство](#43-теорема-о-разрезе-доказательство) + - [Теорема о разрезе](#теорема-о-разрезе) + - [4.4. Алгоритм Прима. Аналогия с алгоритмом Дейкстры. Оценка времени работы для различных реализаций очереди с приоритетом: бинарная куча, Фибоначчиева куча (последнее без доказательства)](#44-алгоритм-прима-аналогия-с-алгоритмом-дейкстры-оценка-времени-работы-для-различных-реализаций-очереди-с-приоритетом-бинарная-куча-фибоначчиева-куча-последнее-без-доказательства) + - [Алгоритм Прима](#алгоритм-прима) + - [Аналогия с алгоритмом Дейкстры](#аналогия-с-алгоритмом-дейкстры) + - [Оценка времени работы для различных реализаций очереди с приоритетом: бинарная куча, Фибоначчиева куча](#оценка-времени-работы-для-различных-реализаций-очереди-с-приоритетом-бинарная-куча-фибоначчиева-куча) + - [4.5. Алгоритм Прима. Доказательство корректности с помощью теоремы о разрезе](#45-алгоритм-прима-доказательство-корректности-с-помощью-теоремы-о-разрезе) + - [Доказательство корректности с помощью теоремы о разрезе](#доказательство-корректности-с-помощью-теоремы-о-разрезе) + - [4.6. Алгоритм Краскала. Доказательство корректности. Оценка времени работы](#46-алгоритм-краскала-доказательство-корректности-оценка-времени-работы) + - [Алгоритм Краскала](#алгоритм-краскала) + - [Доказательство корректности](#доказательство-корректности-1) + - [Оценка времени работы](#оценка-времени-работы-2) + - [4.7. Система непересекающихся множеств. Эвристика потенциалов без доказательства. Эвристика сжатия пути без доказательства. Почти константное время работы](#47-система-непересекающихся-множеств-эвристика-потенциалов-без-доказательства-эвристика-сжатия-пути-без-доказательства-почти-константное-время-работы) + - [4.8. Алгоритм Борувки. Доказательство(полное). Оценка времени работы](#48-алгоритм-борувки-доказательствополное-оценка-времени-работы) + - [Алгоритм Борувки](#алгоритм-борувки) + - [Оценка времени работы](#оценка-времени-работы-3) + - [4.9. Приближение решения задачи коммивояжера с помощью минимального остовного дерева](#49-приближение-решения-задачи-коммивояжера-с-помощью-минимального-остовного-дерева) + - [5. Потоки в сетях](#5-потоки-в-сетях) + - [5.1. Определение сети. Определение потока. Физический смысл. Аналогия с законами Кирхгофа. Определение разреза. Понятия потока через разрез](#51-определение-сети-определение-потока-физический-смысл-аналогия-с-законами-кирхгофа-определение-разреза-понятия-потока-через-разрез) + - [Определение сети](#определение-сети) + - [Определение потока](#определение-потока) + - [Физический смысл. Аналогия с законами Кирхгофа](#физический-смысл-аналогия-с-законами-кирхгофа) + - [Определение разреза](#определение-разреза) + - [Понятия потока через разрез](#понятия-потока-через-разрез) + - [5.2. Доказательство факта, что поток через любой разрез одинаковый](#52-доказательство-факта-что-поток-через-любой-разрез-одинаковый) + - [5.3. Понятие остаточной сети. Понятие дополняющего пути. Необходимость отсутствия дополняющего пути для максимальности потока](#53-понятие-остаточной-сети-понятие-дополняющего-пути-необходимость-отсутствия-дополняющего-пути-для-максимальности-потока) + - [Понятие остаточной сети](#понятие-остаточной-сети) + - [Понятие дополняющего пути](#понятие-дополняющего-пути) + - [Необходимость отсутствия дополняющего пути для максимальности потока](#необходимость-отсутствия-дополняющего-пути-для-максимальности-потока) + - [5.4. Теорема Форда-Фалкерсона](#54-теорема-форда-фалкерсона) + - [5.5. Алгоритм Форда-Фалкерсона. Поиск минимального разреза. Пример целочисленной сети, в котором алгоритм работает долго](#55-алгоритм-форда-фалкерсона-поиск-минимального-разреза-пример-целочисленной-сети-в-котором-алгоритм-работает-долго) + - [Алгоритм Форда-Фалкерсона](#алгоритм-форда-фалкерсона) + - [Поиск минимального разреза](#поиск-минимального-разреза) + - [5.6. Алгоритм Эдмондса-Карпа. Доказательство, что кратчайшее расстояние в остаточной сети не уменьшается](#56-алгоритм-эдмондса-карпа-доказательство-что-кратчайшее-расстояние-в-остаточной-сети-не-уменьшается) + - [Алгоритм Эдмондса-Карпа](#алгоритм-эдмондса-карпа) + - [5.7. Общая оценка времени работы алгоритма Эдмондса-Карпа](#57-общая-оценка-времени-работы-алгоритма-эдмондса-карпа) + - [5.8. Слоистая сеть. Алгоритм Диница. Оценка времени работы без доказательства](#58-слоистая-сеть-алгоритм-диница-оценка-времени-работы-без-доказательства) + - [Слоистая сеть](#слоистая-сеть) + - [Алгоритм Диница](#алгоритм-диница) + - [Оценка времени работы без доказательства](#оценка-времени-работы-без-доказательства) + - [6. RMQ. Sparse-table, дерево отрезков. LCA. Декартово дерево по неявному ключу](#6-rmq-sparse-table-дерево-отрезков-lca-декартово-дерево-по-неявному-ключу) + - [6.1. RSQ и RMQ. Sparse-table](#61-rsq-и-rmq-sparse-table) + - [RSQ и RMQ.](#rsq-и-rmq) + - [6.2. Дерево отрезков. Обработка запросов от листьев. Обработка запросов от корня](#62-дерево-отрезков-обработка-запросов-от-листьев-обработка-запросов-от-корня) + - [Дерево отрезков](#дерево-отрезков) + - [Обработка запросов от листьев. Обработка запросов от корня](#обработка-запросов-от-листьев-обработка-запросов-от-корня) + - [6.3. Изменение значения в массиве, обновление дерева отрезков. Множественные операции](#63-изменение-значения-в-массиве-обновление-дерева-отрезков-множественные-операции) + - [Изменение значения в массиве, обновление дерева отрезков.](#изменение-значения-в-массиве-обновление-дерева-отрезков) + - [Множественные операции](#множественные-операции) + - [6.4. LCA. Метод двоичного подъёма](#64-lca-метод-двоичного-подъёма) + - [LCA](#lca) + - [Метод двоичного подъёма](#метод-двоичного-подъёма) + - [6.5. Декартово дерево по неявному ключу. Интерфейс быстрого массива: Доступ к элементу в позиции i, Вставка элемента в позицию i, Удаление элемента из позиции i, Конкатенация двух массивов, разделение массива на два](#65-декартово-дерево-по-неявному-ключу-интерфейс-быстрого-массива-доступ-к-элементу-в-позиции-i-вставка-элемента-в-позицию-i-удаление-элемента-из-позиции-i-конкатенация-двух-массивов-разделение-массива-на-два) + - [Декартово дерево по неявному ключу](#декартово-дерево-по-неявному-ключу) + - [Интерфейс быстрого массива](#интерфейс-быстрого-массива) + - [Конкатенация двух массивов](#конкатенация-двух-массивов) + - [Разделение массива на два](#разделение-массива-на-два) ## 1. Обходы графа @@ -836,3 +898,444 @@ new_d[i][j] = min(d[i][j], d[i][k] + d[k][j]); #### Добавление фиктивного корня и фиктивных ребер для запуска алгоритма Форда-Беллмана Как в случае применения алгоритма Беллмана-Форда для обнаружения отрицательных циклов, существуют два способа сделать вес каждого ребра неотрицательным в произвольной сети без отрицательных циклов. Либо можно начать с истока в каждом сильно связном компоненте, либо добавить фиктивную вершину с ребром нулевой длины в каждую вершину сети. В любом случае получится остовный лес кратчайших путей, которым можно воспользоваться для присвоения весов вершинам (вес пути из корня в данную вершину в ее SPT). + +## 4. Остовные деревья + +### 4.1. Остовное дерево. Построение с помощью обхода в глубину и в ширину + +#### Остовное дерево + +> **Остовное дерево** графа (англ. *Spanning tree*) — это дерево, подграф данного графа, с тем же числом вершин, что и у исходного графа. Неформально говоря, остовное дерево получается из исходного графа удалением максимального числа рёбер, входящих в циклы, но без нарушения связности графа. Остовное дерево включает в себя все n вершин исходного графа и содержит $n-1$ ребро. + +#### Построение с помощью обхода в глубину и в ширину + +Процедуры обхода графа в глубину и в ширину можно простым способом использовать для нахождения остовов. В обоих случаях достижение новой вершины графа $u$ из вершины $v$ вызывает "включение" в остовное дерево ребра $(u, v)$. + +```c++ +void Spisok::Depth_First_Search (Lref r) +//Рекурсивный обход графа в глубину. r - указатель +//на структуру Вирта. +{ + Tref t; + + t = (*r).Trail; cout<<(*r).Key; (*r).Flag = FALSE; + while (t!=NULL) + { if ((*(*t).Id).Flag) Depth_First_Search ((*t).Id); t = (*t).Next; } +} +``` + +```c++ +void Spisok::Ostov_Depth (Lref r) +//Рекурсивный обход графа в глубину, соединенный с +//нахождением ребра остовного дерева. +//r - указатель на структуру Вирта. +{ + Tref t; + Lref s; + + s = r; + t = (*r).Trail; (*r).Flag = FALSE; + while ( t != NULL ) + { if ((*(*t).Id).Flag) + { cout << "(" << s->Key << "," << t->Id->Key << ") "; + Ostov_Depth ((*t).Id); + } + t = (*t).Next; + } +} +``` + +### 4.2. Определение минимального остовного дерева + +> **Минимальное остовное дерево** (англ. *minimum spanning tree*) графа — это его ациклический связный подграф, в который входят все его вершины, обладающий минимальным суммарным весом ребер. Заметим, что граф может содержать несколько минимальных остовных деревьев. + +### 4.3. Теорема о разрезе. Доказательство + +#### Теорема о разрезе + +**Теорема Форда — Фалкерсо́на** — теорема о максимальном потоке в графе, тесно связанная с теоремой Менгера. +Звучит так: величина максимального потока в графе путей равна величине пропускной способности его минимального разреза. + +*Доказательство:* + +**Достаточность**: любой поток между вершинами t и s меньше или равен величине любого сечения. Пусть дан некоторый поток и некоторое сечение. Величина данного потока складывается из величин «грузов», перевозимых по всем возможным путям из вершины t в s. Каждый такой путь обязан иметь общее ребро с данным сечением. Так как по каждому ребру сечения суммарно нельзя перевести «груза» больше, чем его пропускная способность, поэтому сумма всех грузов меньше или равна сумме всех пропускных способностей рёбер данного сечения. Утверждение доказано. + +Отсюда следует, что любой поток меньше или равен величине минимального сечения, а значит и максимальный поток меньше или равен величине минимального сечения. + +### 4.4. Алгоритм Прима. Аналогия с алгоритмом Дейкстры. Оценка времени работы для различных реализаций очереди с приоритетом: бинарная куча, Фибоначчиева куча (последнее без доказательства) + +#### Алгоритм Прима + +> **Алгоритм Прима** (англ. *Prim's algorithm*) — алгоритм поиска минимального остовного дерева (англ. minimum spanning tree, MST) во взвешенном неориентированном связном графе. + +#### Аналогия с алгоритмом Дейкстры + +Данный алгоритм очень похож на алгоритм Дейкстры. Будем последовательно строить поддерево $F$ ответа графе $G$, поддерживая приоритетную очередь $Q$ из вершин $G \setminus F$, в которой ключом для вершины $v$ является $\min(\omega(uv) (u \in V(F), (u, v) \in E(G)))$ — вес минимального ребра из вершин $F$ в вершины $G \setminus F$. Также для каждой вершины в очереди будем хранить $p(v)$ — вершину $u$, на которой достигается минимум в определении ключа. Дерево $F$ поддерживается неявно, и его ребра — это пары $(v,p(v))$, где $v \in G \setminus {r} \setminus Q$, а $r$ — корень $F$. Изначально $F$ пусто и значения ключей у всех вершин равны $+\infty$. Выберём произвольную вершину $r$ и присвоим её ключу значение 0. На каждом шаге будем извлекать минимальную вершину $v$ из приоритетной очереди и релаксировать все ребра $(v, u)$, такие что $u \in Q$, выполняя при этом операцию $\rm decreaseKey$ над очередью и обновление $p(v)$. Ребро $(v,p(v))$ при этом добавляется к ответу. + +#### Оценка времени работы для различных реализаций очереди с приоритетом: бинарная куча, Фибоначчиева куча + +Двоичная куча - $O(ElogV)$ +(Чтобы упростить операцию $\rm decreaseKey$ можно написать кучу на основе сбалансированного бинарного дерева поиска. Тогда просто удалим вершину и добавим ее обратно уже с новым ключом. Асимптотика таких преобразований $O(logn)$. Если же делать с бинарной кучей, то вместо операции $\rm decreaseKey$, будем всегда просто добавлять вершину с новым ключом, если из кучи достали вершину с ключом, значение которого больше чем у нее уже стоит, просто игнорировать. Вершин в куче будет не больше $2 \cdot n$, следовательно, операция $\rm extractMin$ будет выполняться за $O(log(\cdot n))$, что равно $O(logn)$. Максимальное количество вершин, которое мы сможем достать, равняется количеству ребер, то есть m, поэтому общая асимптотика составит $O(mlogn)$, что хорошо только на разреженных графах.) + +Фибоначчиева куча - $O(VlogV+E)$ + +### 4.5. Алгоритм Прима. Доказательство корректности с помощью теоремы о разрезе + +Приведем еще раз теорему о разрезе: +> Для любого разбиения множества вершин $V = A \cup B$, существует минимальный остов, содержащий $e$ – минимальное по весу ребро, проходящее через разрез $\langle A, B \rangle$ + +Доказательство. Возьмём минимальный остов без $e$. Добавим $e$, получится цикл. Два ребра этого цикла проходят через разрез $\langle A, B \rangle$. Старое неменьше $e$, удалим его. Если же в процессе построения $\rm min$ остова уже известно подмножество рёбер будущего $\rm min$ остова, эти рёбра задают компоненты связности. Мы можем сжать компоненты в вершины и для конденсации применить лемму о разрезе. + +Следствие $X$ – подмножество рёбер некого минимального остова. Зафиксируем разрез $V = A \cup B$ такой, что все рёбра $X$ не пересекают разрез. Тогда $\exist$ минимальный остов, содержащий $X \cup \{e\}$, где $e$ – минимальное по весу ребро, проходящее через разрез $\langle A, B \rangle$. + +#### Доказательство корректности с помощью теоремы о разрезе + +Разрез – текущее множество $A$ и дополнение $V \setminus A$. + +### 4.6. Алгоритм Краскала. Доказательство корректности. Оценка времени работы + +#### Алгоритм Краскала + +Будем последовательно строить подграф $F$ графа $G$ ("растущий лес"), пытаясь на каждом шаге достроить $F$ до некоторого $MST$. Начнем с того, что включим в $F$ все вершины графа $G$. Теперь будем обходить множество $E(G)$ в порядке неубывания весов ребер. Если очередное ребро $e$ соединяет вершины одной компоненты связности $F$, то добавление его в остов приведет к возникновению цикла в этой компоненте связности. В таком случае, очевидно, $e$ не может быть включено в $F$. Иначе $e$ соединяет разные компоненты связности $F$, тогда существует $\langle S, T \rangle$ разрез такой, что одна из компонент связности составляет одну его часть, а оставшаяся часть графа — вторую. Тогда $e$ — минимальное ребро, пересекающее этот разрез. Значит, из леммы о безопасном ребре следует, что $e$ является безопасным, поэтому добавим это ребро в $F$. На последнем шаге ребро соединит две оставшиеся компоненты связности, полученный подграф будет минимальным остовным деревом графа $G$. Для проверки возможности добавления ребра используется система непересекающихся множеств. + +##### Доказательство корректности + +Обратимся к теореме о разрезе из Пункта 4.5 +Добавляем ребро $(a, b)$. Разрез – любой такой, что $(a, b)$ через него проходит. + +##### Оценка времени работы + +$O(ElogE)$. + +### 4.7. Система непересекающихся множеств. Эвристика потенциалов без доказательства. Эвристика сжатия пути без доказательства. Почти константное время работы + +### 4.8. Алгоритм Борувки. Доказательство(полное). Оценка времени работы + +#### Алгоритм Борувки + +Алгоритм состоит из нескольких шагов: + +1. Изначально каждая вершина графа $G$ — тривиальное дерево, а ребра не принадлежат никакому дереву. +2. Для каждого дерева $T$ найдем минимальное инцидентное ему ребро. Добавим все такие ребра. +3. Повторяем шаг 2 пока в графе не останется только одно дерево $T$. + +Данный алгоритм может работать неправильно, если в графе есть ребра равные по весу. Например, полный граф из трех вершин, вес каждого ребра равен один. В $T$ могут быть добавлены все три ребра. Избежать эту проблему можно, например, выбирая в первом пункте среди ребер, равных по весу, ребро с наименьшим номером. + +**Доказательство:** + +Обратимся к теореме о разрезе из Пункта 4.5 +Рёбра, которые хочет добавить Борувка, разобьём на компоненты связности. Каждая компонента – дерево, рёбра компоненты будем добавлять по одному от корня к листьям. Добавляя +очередное ребро ведущее в лист $v$, используем разрез $\langle \{v\}, V \setminus \{v\} \rangle$ и лемму о разрезе. + +##### Оценка времени работы + +На $i$-ой итерации внешнего цикла каждая компонента состоит как минимум из двух компонент из $(i−1)$-й итерации. Значит, на каждой итерации число компонент уменьшается как минимум в 2 раза. Тогда внешний цикл повторяется $O(logV)$ раз, так как количество компонент изначально равно количеству вершин. Что же касается внутреннего цикла, то он выполняется за $O(E)$, где $E$ — количество рёбер в исходном графе. Следовательно конечное время работы алгоритма $O(ElogV)$. + +### 4.9. Приближение решения задачи коммивояжера с помощью минимального остовного дерева + +> **Алгоритм Кристофидеса** или **алгоритм Кристофидеса-Сердюкова** — это алгоритм поиска приближённых решений задачи коммивояжёра для случаев, когда расстояния образуют метрическое пространство (симметричны и удовлетворяют неравенству треугольника). + +Алгоритм является аппроксимационным алгоритмом, который гарантирует, что решения находятся в пределах 3/2 от длины оптимального решения. Алгоритм назван именем Никоса Кристофидеса и Анатолия Ивановича Сердюкова, которые независимо друг от друга нашли его в 1976, и он обладает лучшим аппроксимационным коэффициентом, который был доказан для задачи коммивояжёра на метрических пространствах общего вида, хотя известны лучшие приближения для некоторых специальных случаев. + +Пусть $G=(V,w)$ будет представителем задачи коммивояжёра. То есть $G$ является полным графом на множестве вершин $V$, а функция $w$ назначает неотрицательные вещественные веса каждому ребру графа $G$. +Алгоритм можно описать на псевдокоде следующим образом: + +1. Создаём минимальное остовное дерев $T$ графа $G$. +2. Пусть $O$ будет набором вершин с нечётными степенями в $T$. Согласно лемме о рукопожатиях, $O$ имеет чётное число вершин. +3. Находим совершенное паросочетание $M$ минимального веса в порождённом подграфе, заданным вершинами из $O$. +4. Комбинируем рёбра $M$ и $T$ с образованием связного мультиграфа $H$, в котором каждая вершина имеет чётную степень. +5. Образуем эйлеров цикл в $H$. +6. Преобразуем цикл, найденный на предыдущем шаге, в гамильтонов цикл путём пропуска повторяющихся вершин (сокращение). + +## 5. Потоки в сетях + +### 5.1. Определение сети. Определение потока. Физический смысл. Аналогия с законами Кирхгофа. Определение разреза. Понятия потока через разрез + +#### Определение сети + +> **Сеть** (англ. *flow network*) представляет собой ориентированный граф, в котором каждое ребро имеет положительную пропускную способность (англ. *capacity*). + +#### Определение потока + +> **Потоком** (англ. *flow*) $f$ в $G$ является действительная функция $f: V \times V \rightarrow R$, удоволетворяющая условиям: + +1) $f(u,v) = −f(v,u)$ (антисимметричность) +2) $f(u,v) \leq c(u,v)$ (ограничение пропускной способности), если ребра нет, то $f(u,v)=0$; +3) $\Sigma v \cdot f(u,v) = 0$ для всех вершин $u$, кроме $s$ и $t$ (закон сохранения потока). + +> Величина потока $f$ определяется как $|f|=\Sigma v \in V \cdot f(s,v)$. + +#### Физический смысл. Аналогия с законами Кирхгофа + +#### Определение разреза + +> $(s,t)$ - **разрезом** (англ. *s-t cut*) $\langle S,T \rangle$ в сети $G$ называется пара множеств $S$, $T$, удоволетворяющих условиям: $s \in S$,$t \in T$ и $S = V \setminus T$. + +#### Понятия потока через разрез + +> **Поток в разрезе** (англ. *flow in the cut*) $\langle S,T \rangle$ обозначается $f(S,T)$ и вычисляется по формуле: $f(S,T)= \Sigma u \in S \cdot \Sigma v \in T \cdot f(u,v)$. + +### 5.2. Доказательство факта, что поток через любой разрез одинаковый + +$f(S,T) = f(S,V) − f(S,S) = f(S,V) = f(S \setminus s,V) + f(s,V) = f(s,V) = |f|$ + +1-е равенство выполняется, так как суммы не пересекаются: $f(S,V)=f(S,S)+f(S,T)$ +2-е равенство выполняется из-за антисимметричности: $f(S,S)=−f(S,S)=0$ +3-е равенство выполняется, как и 1-е, из-за непересекающихся сумм +4-е равенство выполняется из-за сохранения потока + +### 5.3. Понятие остаточной сети. Понятие дополняющего пути. Необходимость отсутствия дополняющего пути для максимальности потока + +#### Понятие остаточной сети + +Если задана некоторая транспортная сеть $G$ и поток $f$, то **остаточная сеть** $G_f$ - это сеть, состоящая из ребер с пропускными способностями, указывающими, как могут меняться потоки через ребра $G$. + +#### Понятие дополняющего пути + +Для заданной транспортной сети и потока **дополняющим путем** (англ. *augmenting path*) является простой путь из истока в сток в остаточной сети. + +#### Необходимость отсутствия дополняющего пути для максимальности потока + +В случае существования дополняющего пути поток можно увеличить. + +### 5.4. Теорема Форда-Фалкерсона + +> **Теорема Форда — Фалкерсо́на** — теорема о максимальном потоке в графе, тесно связанная с теоремой Менгера. Звучит так: величина максимального потока в графе путей равна величине пропускной способности его минимального разреза. + +### 5.5. Алгоритм Форда-Фалкерсона. Поиск минимального разреза. Пример целочисленной сети, в котором алгоритм работает долго + +#### Алгоритм Форда-Фалкерсона + +Идея алгоритма заключается в следующем. Изначально величине потока присваивается значение 0: $f(u,v)=0$ для всех $u, v \in V$. Затем величина потока итеративно увеличивается посредством поиска увеличивающего пути (путь от источника $s$ к стоку $t$, вдоль которого можно послать ненулевой поток). В данной статье рассматривается алгоритм, осуществляющий этот поиск с помощью обхода в глубину (dfs). Процесс повторяется, пока можно найти увеличивающий путь. + +**Неформальное описание:** + +1. Обнуляем все потоки. Остаточная сеть изначально совпадает с исходной сетью. +2. В остаточной сети находим любой путь из источника в сток. Если такого пути нет, останавливаемся. +3. Пускаем через найденный путь (он называется увеличивающим путём или увеличивающей цепью) максимально возможный поток: + 1. На найденном пути в остаточной сети ищем ребро с минимальной пропускной способностью $c_{min}$. + 2. Для каждого ребра на найденном пути увеличиваем поток на $c_{min}$, а в противоположном ему — уменьшаем на $c_{min}$. + 3. Модифицируем остаточную сеть. Для всех рёбер на найденном пути, а также для противоположных (антипараллельных) им рёбер, вычисляем новую пропускную способность. Если она стала ненулевой, добавляем ребро к остаточной сети, а если обнулилась, стираем его. +4. Возвращаемся на шаг 2. + +#### Поиск минимального разреза + +Алгоритм Форда-Фалкерсона использует поиск в ширину для нахождения увеличивающего пути в сети и постепенно увеличивает поток через этот путь, пока не будет достигнут максимальный поток. Затем алгоритм ищет минимальный разрез, основываясь на значении потока. + +### 5.6. Алгоритм Эдмондса-Карпа. Доказательство, что кратчайшее расстояние в остаточной сети не уменьшается + +#### Алгоритм Эдмондса-Карпа + +> **Алгоритм Эдмондса-Карпа** является реализацией метода Форда-Фалкерсона, в которой в качестве дополняющего пути выбирается кратчайший по рёбрам путь в остаточной сети (длины всех рёбер равны 1). + +**Описание:** + +1. Положим все потоки равными нулю. Остаточная сеть изначально совпадает с исходной сетью. +2. В остаточной сети находим кратчайший путь из источника в сток. Если такого пути нет, останавливаемся. +3. Пускаем через найденный путь (он называется увеличивающим путём или увеличивающей цепью) максимально возможный поток: + 1. На найденном пути в остаточной сети ищем ребро с минимальной пропускной способностью $c_{min}$. + 2. Для каждого ребра на найденном пути увеличиваем поток на $c_{min}$, а в противоположном ему — уменьшаем на $c_{min}$. + 3. Модифицируем остаточную сеть. Для всех рёбер на найденном пути, а также для противоположных им рёбер, вычисляем новую пропускную способность. Если она стала ненулевой, добавляем ребро к остаточной сети, а если обнулилась, стираем его. +4. Возвращаемся на шаг 2. + +**Доказательство, что кратчайшее расстояние в остаточной сети не уменьшаетcя:** + +**Лемма:** + +> Кратчайшее расстояние от истока до каждой вершины не уменьшается с выполнением каждой итерации, т.е. $level_{i+1}[v] >= level_i[v]$, где нижний индекс обозначает номер фазы, перед которой взяты значения этих переменных. + +**Доказательство:** + +Зафиксируем произвольную фазу $i$ и произвольную вершину $v$ и рассмотрим любой кратчайший $s - v$ путь $P$ в сети $G^R_{i+1}$ (напомним, так мы обозначаем остаточную сеть, взятую перед выполнением $i+1$-ой фазы). Очевидно, длина пути $P$ равна $level_{i+1}[v]$. + +Заметим, что в остаточную сеть $G^R_{i+1}$ могут входить только рёбра из $G^R$, а также рёбра, обратные рёбрам из $G^R$ (это следует из определения остаточной сети). Рассмотрим два случая: + +Путь $P$ содержит только рёбра из $G^R$. Тогда, понятно, длина пути $P$ больше либо равна $level_i[v]$ (потому что $level_i[v]$ по определению — длина кратчайшего пути), что и означает выполнение неравенства. Путь $P$ содержит как минимум одно ребро, не содержащееся в $G^R$ (но обратное какому-то ребру из $G^R$). Рассмотрим первое такое ребро; пусть это будет ребро $(u,w)$. $s \rightarrow u \rightarrow w$ + +Мы можем применить нашу лемму к вершине $u$, потому что она подпадает под первый случай; итак, мы получаем неравенство $level_{i+1}[u] >= level_i[u]$. + +Теперь заметим, что поскольку ребро $(u, w)$ появилось в остаточной сети только после выполнения $i$-ой фазы, то отсюда следует, что вдоль ребра $(w, u)$ был дополнительно пропущен какой-то поток; следовательно, ребро $(w, u)$ принадлежало слоистой сети перед $i$-ой фазой, а потому $level_i[u] = level_i[w] + 1$. Учтём, что по свойству кратчайших путей $level_{i+1}[w] = level_{i+1}[u] + 1$, и объединяя это равенство с двумя предыдущими неравенствами, получаем: +$level_{i+1}[w] >= level_i[w] + 2$. + +Теперь мы можем применять те же самые рассуждения ко всему оставшемуся пути до $v$ (т.е. что каждое инвертированное ребро добавляет к $\rm level$ как минимум два), и в итоге получим требуемое неравенство. + +### 5.7. Общая оценка времени работы алгоритма Эдмондса-Карпа + +Сложность алгоритма Эдмондса-Карпа равна $O(VE^2)$. + +### 5.8. Слоистая сеть. Алгоритм Диница. Оценка времени работы без доказательства + +#### Слоистая сеть + +> **Слоистая сеть** — это сеть состоящая только из таких рёбер $(v, u)$ сети $G$, что $d(v) + 1 = d(u)$, где $d$ - расстояние от истока. + +#### Алгоритм Диница + +**Схема алгоритма:** + +1. Построить слоистую сеть по остаточной, если сток не достижим то завершить алгоритм +2. Пустить блокирующий поток в слоистой сети +3. Если алгоритм завершился, то в остаточной сети сток не достижим из истока, а значит не существует увеличивающего пути, следовательно найденный поток максимален. + +#### Оценка времени работы без доказательства + +Так как расстояние строго увеличивается количество итераций алгоритма = $O(V)$. В зависимости от выбора алгоритма поиска блокирующего потока время данного алгоритма может составить $O(VE^2), O(V^2E)$ или $O(V^3)$. + +## 6. RMQ. Sparse-table, дерево отрезков. LCA. Декартово дерево по неявному ключу + +### 6.1. RSQ и RMQ. Sparse-table + +#### RSQ и RMQ. +Дан числовой массив: $A[0]$, $A[1]$, ..., $A[N−1$.$ + +Нужно отвечать на запросы: + +- для данных $L$ и $R$ вычислить сумму $A[L] + ... + A[R−1]$; +- (опционально) обновить элемент: $A[i] := x$. + +Операция — не обязательно сумма, а элементы — не обязательно числа. + +- Сумма $\rightarrow$ RSQ (range sum query) +- Минимум $\rightarrow$ RMQ (range minimum query) + +### 6.2. Дерево отрезков. Обработка запросов от листьев. Обработка запросов от корня + +#### Дерево отрезков + +Дерево отрезков — это структура данных, которая позволяет эффективно (т.е. за асимптотику $O(log n)$) реализовать операции следующего вида: нахождение суммы/минимума элементов массива в заданном отрезке ($a[l ... r]$, где $l$ и $r$ поступают на вход алгоритма), при этом дополнительно возможно изменение элементов массива: как изменение значения одного элемента, так и изменение элементов на целом подотрезке массива (т.е. разрешается присвоить всем элементам $a[l ... r]$ какое-либо значение, либо прибавить ко всем элементам массива какое-либо число). + +Вообще, дерево отрезков — очень гибкая структура, и число задач, решаемых ей, теоретически неограниченно. Помимо приведённых выше видов операций с деревьями отрезков, также возможны и гораздо более сложные операции (см. раздел "Усложнённые версии дерева отрезков"). В частности, дерево отрезков легко обобщается на большие размерности: например, для решения задачи о поиске суммы/минимума в некотором подпрямоугольнике данной матрицы (правда, уже только за время $O(log^2 n)$). + +Важной особенностью деревьев отрезков является то, что они потребляют линейный объём памяти: стандартному дереву отрезков требуется порядка $4n$ элементов памяти для работы над массивом размера $n$. + +#### Обработка запросов от листьев. Обработка запросов от корня + +В дереве отрезков обработка запросов может осуществляться как от листьев (bottom-up), так и от корня (top-down). Это связано с тем, как информация распространяется вдоль дерева при выполнении запросов. + +1. **Обработка запросов от листьев (Bottom-Up):** + - Идея заключается в том, что сначала вычисляются значения для листьев (первоначальные элементы массива), а затем эта информация постепенно передается вверх по дереву для расчета значений на более высоких уровнях. + - Когда поступает запрос на определенный отрезок, начинаются вычисления от соответствующих листьев. Затем результаты объединяются на более высоких уровнях дерева, пока не достигнется корень. + - Этот подход обеспечивает эффективность расчетов для запросов на отрезках. + +2. **Обработка запросов от корня (Top-Down):** + - Здесь запрос начинается с корня дерева, и информация передается вниз по дереву до тех пор, пока не достигнут нужные листья (или пока не выполнены условия запроса). + - Этот метод может быть полезен в случаях, когда запросы связаны с каким-то условием, которое позволяет пропускать некоторые поддеревья, не вычисляя их полностью. + - Позволяет оптимизировать запросы, если известно, что результаты на некоторых уровнях дерева не повлияют на результат запроса. + +Реализация обработки запросов от листьев или от корня зависит от конкретных требований задачи и структуры дерева отрезков. В некоторых случаях может быть полезно использовать комбинацию обоих методов для оптимизации производительности и удовлетворения специфических требований запросов. + +### 6.3. Изменение значения в массиве, обновление дерева отрезков. Множественные операции + +#### Изменение значения в массиве, обновление дерева отрезков. + +Напомним, что запрос обновления получает на вход индекс $i$ и значение $x$, и перестраивает дерево отрезков таким образом, чтобы оно соответствовало новому значению $A[i] := x$. Этот запрос должен также выполняться за время $O(log n)$. + +Это более простой запрос по сравнению с запросом подсчёта суммы. Дело в том, что элемент $A[i]$ участвует только в относительно небольшом числе вершин дерева отрезков: а именно, в $O(log n)$ вершинах — по одной с каждого уровня. + +Тогда понятно, что запрос обновления можно реализовать как рекурсивную функцию: ей передаётся текущая вершина дерева отрезков, и эта функция выполняет рекурсивный вызов от одного из двух своих сыновей (от того, который содержит позицию i в своём отрезке), а после этого — пересчитывает значение суммы в текущей вершине точно таким же образом, как мы это делали при построении дерева отрезков (т.е. как сумма значений по обоим сыновьям текущей вершины). + +```c++ +void update (int v, int tl, int tr, int pos, int new_val) { + if (tl == tr) + t[v] = new_val; + else { + int tm = (tl + tr) / 2; + if (pos <= tm) + update (v*2, tl, tm, pos, new_val); + else + update (v*2+1, tm+1, tr, pos, new_val); + t[v] = t[v*2] + t[v*2+1]; + } +} +``` + +#### Множественные операции + +Множественные операции в дереве отрезков относятся к расширенной концепции структуры данных "дерево отрезков", которая позволяет эффективно выполнять несколько операций на отрезках массива или последовательности данных. Обычно дерево отрезков предназначено для выполнения операций типа запроса на сумму, минимум, максимум на определенном подотрезке массива. Однако, при необходимости, можно расширить возможности дерева отрезков для поддержки нескольких операций. + +Примеры множественных операций в дереве отрезков могут включать в себя: + +1. **Сумма и обновление (Range Sum Query and Range Update):** Помимо запросов на сумму элементов на определенном отрезке, можно добавить операции обновления, изменяющие значения элементов на определенном отрезке. + +2. **Минимум/максимум и обновление (Range Minimum/Maximum Query and Range Update):** Поддержка запросов на поиск минимума или максимума на определенном отрезке, а также операций обновления значений элементов. + +3. **Подсчет количества элементов в диапазоне с заданным условием (Count Elements in Range with a Given Condition):** Дерево отрезков может быть настроено для подсчета количества элементов, удовлетворяющих определенному условию, на определенном отрезке. + +4. **Операции нахождения k-го порядкового статистического элемента (K-th Order Statistics Operations):** Поиск $k$-го минимального/максимального элемента на определенном отрезке. + +5. **Сложные агрегатные функции:** Различные агрегатные функции, такие как нахождение среднего значения, медианы и др., могут быть реализованы с использованием множественных операций. + +Реализация множественных операций требует изменений в структуре дерева отрезков и логике обработки запросов и обновлений. Это может потребовать более сложного кода, но в то же время предоставляет более широкий спектр возможностей для решения различных задач. + +### 6.4. LCA. Метод двоичного подъёма + +#### LCA + +Наименьший общий предок (нижайший общий предок) вершин $u$ и $v$ в корневом дереве $T$ — наиболее удалённая от корня дерева вершина, лежащая на обоих путях от $u$ и $v$ до корня, т. е. являющаяся предком обеих вершин. Общепринятое сокращение — $LCA$ от англ. *lowest (least) common ancestor*. + +#### Метод двоичного подъёма + +> **Метод двоичного подъёма** — один из самых простых методов для решения задачи LCA в online. Он не использует метод решения задачи RMQ и основан на методе динамического программирования. + +```c++ +function preprocess(): + int[] p = dfs(0) + for i = 1 to n + dp[i][0] = p[i] + for j = 1 to log(n) + for i = 1 to n + dp[i][j] = dp[dp[i][j - 1]][j - 1] + + int lca(int v, int u): + if d[v] > d[u] + swap(v, u) + for i = log(n) downto 0 + if d[dp[u][i]] - d[v] >= 0 + u = dp[u][i] + if v == u + return v + for i = log(n) downto 0 + if dp[v][i] != dp[u][i] + v = dp[v][i] + u = dp[u][i] + return p[v] +``` + +### 6.5. Декартово дерево по неявному ключу. Интерфейс быстрого массива: Доступ к элементу в позиции i, Вставка элемента в позицию i, Удаление элемента из позиции i, Конкатенация двух массивов, разделение массива на два + +#### Декартово дерево по неявному ключу + +Декартово дерево по неявному ключу - это структура данных, объединяющая свойства декартова дерева и быстрого массива (cartesian tree on implicit key). Декартово дерево основано на двух параметрах: приоритете (обычно случайном) и ключе элемента. В случае декартова дерева по неявному ключу, элементы упорядочены по порядку, заданному индексами массива. + +#### Интерфейс быстрого массива + +1. **Доступ к элементу в позиции i:** + - Так как элементы упорядочены по неявному ключу (порядковому номеру), доступ к элементу в позиции i выполняется, обращаясь к элементу с ключом i. + +2. **Вставка элемента в позицию i:** + - Вставка элемента в позицию i может быть выполнена путем создания нового узла с соответствующим приоритетом и неявным ключом i, затем соединения этого нового узла с существующими поддеревьями вокруг позиции i. + +3. **Удаление элемента из позиции i:** + - Удаление элемента из позиции i подразумевает разделение декартова дерева на два поддерева: одно до позиции i и второе после позиции i. Затем оба поддерева могут быть объединены, игнорируя узел с ключом i. + +Декартово дерево по неявному ключу обеспечивает эффективный доступ к элементам по индексу, а также эффективную вставку и удаление элементов, поддерживая при этом свойства декартова дерева. + +#### Конкатенация двух массивов + +Декартово дерево можно использовать для эффективной конкатенации двух массивов и разделения массива на две части, представляя массив в виде декартова дерева по неявному ключу. + +Для конкатенации двух массивов, представленных в виде декартова дерева, можно выполнить следующие шаги: + +1. Создать новый узел, который станет корнем нового дерева. +2. Как корень нового дерева выбрать узел с максимальным приоритетом из обоих деревьев. +3. Сделать корень дерева с максимальным приоритетом корнем нового дерева. +4. Поддерево с меньшим приоритетом становится левым (или правым) поддеревом корня нового дерева. +5. Рекурсивно повторить процесс для поддеревьев, соответствующих двум исходным деревьям. + +#### Разделение массива на два + +Для разделения массива на две части в декартовом дереве, можно воспользоваться тем фактом, что приоритеты узлов определяют порядок элементов. Мы ищем узел, чей неявный ключ соответствует индексу разделения. + +1. Выполняем поиск в декартовом дереве по индексу разделения. +2. Получаем поддерево с корнем, у которого неявный ключ равен индексу разделения. +3. Разделяем это поддерево на два: левое поддерево становится левой частью, а правое поддерево становится правой частью. From f82496621f691ec8736d267324afd714a20af24a Mon Sep 17 00:00:00 2001 From: Egor Ivanov <56685477+sibenshtern@users.noreply.github.com> Date: Sun, 7 Jan 2024 05:58:54 +0000 Subject: [PATCH 5/7] update theory --- doc/Theory.html | 1387 +++++++++++++++++++++++++++++++++++++++++++++++ doc/Theory.md | 279 +++++----- 2 files changed, 1532 insertions(+), 134 deletions(-) create mode 100644 doc/Theory.html diff --git a/doc/Theory.html b/doc/Theory.html new file mode 100644 index 0000000..f40dda1 --- /dev/null +++ b/doc/Theory.html @@ -0,0 +1,1387 @@ + + + + + Теория по алгоритмам + + + + + + + + + + +

Теория по алгоритмам

+

Содержание

+ +

1. Обходы графа

+

1.1. Ориентированный граф, псевдограф. Неориентированный граф, псевдограф. Связность в неориентированном графе, компоненты связности. Слабая и сильная связность в ориентированном графе. Компоненты слабой, сильной связности

+

Ориентированный граф

+
+

Ориентированным графом (англ. directed graph) GG называется пара G=(V,E)G = (V, E), где VV — множество вершин (англ. vertices), а EV×VE \subset V \times V — множество рёбер.

+
+
+

Псевдограф(мультиорграф) — это ориентированный граф, в котором разрешены кратные дуги, то есть дуги, имеющие те же начальные и конечные вершины.

+
+

Псевдографом GG называется упорядоченная пара G:=(V,A)G := (V, A), в которой VV — множество вершин, AA — мультимножество упорядоченных пар вершин. Элементы этого множества называются дугами.

+
+

Ориентированный граф называется слабо связным, если соответствующий неориентированный граф является связным;

+
+
+

Ориентированный граф называется сильно связным, если всякая вершина vv достижима из любой другой вершины vv;

+
+
+

Компонентой сильной связности ориентированного графа называется максимальный по включению сильно связный подграф. Другими словами, это подграф, любые две вершины которого принадлежат какому-либо циклу, и содержащий все такие циклы для своих вершин.

+
+
+

Компонентой слабой связности ориентированного графа называют его максимальный слабо связный подграф.

+
+

Неориентированный граф

+
+

Неориентированным графом (англ. undirected graph) GG называется пара G=(V,E)G = (V, E), где VV — множество вершин, а Ev,u:v,uVE \subset {{v,u} : v,u \in V} — множество рёбер.

+
+

Формально, псевдографом (мультифграфом) GG называется упорядоченная пара G:=(V,E)G := (V, E), в которой VV — множество вершин, EE — мультимножество неупорядоченных пар вершин. Элементы этого множества называются рёбрами.

+
+

Неориентированный граф называется связным, если все его вершины достижимы из некоторой вершины (эквивалентно, из любой его вершины).

+
+
+

Компонентой связности неориентированного графа называется максимальный по включению связный подграф

+
+

1.2. Обход в глубину. Цвета вершин. Времена входа и выхода. Лемма о белых путях (с доказательством)

+

Обход в глубину

+
+

Обход в глубину (поиск в глубину, англ. Depth-First Search, DFS) — один из основных методов обхода графа, часто используемый для проверки связности, поиска цикла и компонент сильной связности и для топологической сортировки.

+
+
#include <vector>
+
+std::vector<std::vector<int>> graph; // граф
+int vertex_count; // число вершин
+
+std::vector<int> color; // цвет вершины (0, 1, или 2)
+
+std::vector<int> time_in, time_out; // "времена" захода и выхода из вершины
+int dfs_timer = 0; // "таймер" для определения времён
+
+void dfs (int vertex) {
+  time_in[vertex] = dfs_timer++;
+  color[vertex] = 1;
+  for (int u : graph[vertex])
+    if (color[u] == 0)
+      dfs(u);
+  color[v] = 2;
+  time_out[v] = dfs_timer++;
+}
+
+

Лемма о белых путях

+
+

Лемма: Не существует такого момента выполнения поиска в глубину, в который бы существовало ребро из черной вершины в белую.

+
+

Доказательство: Пусть в процессе выполнения процедуры DFS нашлось ребро из черной вершины vv в белую вершину u. Рассмотрим момент времени, когда мы запустили DFS(v). В этот момент вершина vv была перекрашена из белого в серый, а вершина u была белая. Далее в ходе выполнения алгоритма будет запущен DFS(u), поскольку обход в глубину обязан посетить все белые вершины, в которые есть ребро из v. По алгоритму вершина vv будет покрашена в черный цвет тогда, когда завершится обход всех вершин, достижимых из нее по одному ребру, кроме тех, что были рассмотрены раньше нее. Таким образом, вершина vv может стать черной только тогда, когда DFS выйдет из вершины u, и она будет покрашена в черный цвет. Получаем противоречие.

+

1.3. Проверка связности неориентированного графа. Поиск цикла в неориентированном и ориентированном графе. Топологическая сортировка

+

Проверка связности неориентированного графа

+

Небольшая модификация алгоритма обхода в глубину, в которой будем возвращать количество посещенных вершин. Запустим такой DFS от некоторой вершины графа GG, если его результат равен V|V|, то мы побывали во всех вершинах графа, а следовательно он связен, иначе какие-то вершины остались непосещенными. Работает алгоритм за O(V+E)O(|V|+|E|)

+
std::vector<std::vector<int>> graph; // граф
+
+// visited — массив цветов вершин  
+int dfs(int vertex, std::vector<bool> visited):              
+  int visitedVertices = 1
+  visited[vertex] = true // помечаем вершину как пройденную
+  for (auto u : graph[vertex]) // проходим по смежным с u вершинам
+    // проверяем, не находились ли мы ранее в выбранной вершине
+    if (!visited[v])
+      visitedVertices += dfs(v, visited)
+    return visitedVertices
+
+

Поиск цикла в неориентированном и ориентированном графе

+

Условие:

+

Пусть дан ориентированный или неориентированный граф без петель и кратных рёбер. Требуется проверить, является ли он ациклическим, а если не является, то найти любой цикл.

+

Решение:

+

Произведём серию поисков в глубину в графе. Т.е. из каждой вершины, в которую мы ещё ни разу не приходили, запустим поиск в глубину, который при входе в вершину будет красить её в серый цвет, а при выходе - в чёрный. И если поиск в глубину пытается пойти в серую вершину, то это означает, что мы нашли цикл (если граф неориентированный, то случаи, когда поиск в глубину из какой-то вершины пытается пойти в предка, не считаются).

+
#include <iostream>
+#include <vector>
+
+int n;
+std::vector<vector<int>> graph;
+std::vector<int> color;
+std::vector<int> parent;
+int cycle_start, cycle_end;
+
+bool dfs(int vertex) {
+  color[vertex] = 1;
+  for (size_t i = 0; i < graph[vertex].size(); ++i) {
+    int $to$  = graph[vertex][i];
+    if (color[to] == 0) {
+      parent[to] = vertex;
+      if (dfs(to)) return true;
+    }
+    else if (color[to] == 1) {
+      cycle_end = vertex;
+      cycle_start = $to$ ;
+      return true;
+    }
+  }
+  color[vertex] = 2;
+  return false;
+}
+
+int main() {
+  // чтение графа
+
+  parent.assign(n, -1);
+  color.assign(n, 0);
+  cycle_start = -1;
+  for (int i = 0; i < n; ++i)
+    if (dfs(i))
+      break;
+
+  if (cycle_st == -1)
+    std::cout << "Acyclic" << "\n";
+  else {
+    std::cout << "Cyclic" << "\n";
+    std::vector<int> cycle;
+    cycle.push_back(cycle_start);
+    for (int vertex = cycle_end; vertex != cycle_start; vertex = parent[vertex])
+      cycle.push_back(vertex);
+    cycle.push_back(cycle_start);
+    reverse(cycle.begin(), cycle.end());
+    for (size_t i = 0; i < cycle.size(); ++i)
+      std::cout << cycle[i]+1 << " ";
+  }
+}
+
+

Топологическая сортировка

+

Условие: +Дан ориентированный граф с n вершинами и m рёбрами. Требуется перенумеровать его вершины таким образом, чтобы каждое рёбро вело из вершины с меньшим номером в вершину с большим. Иными словами, требуется найти перестановку вершин (топологический порядок), соответствующую порядку, задаваемому всеми рёбрами графа. +(Топологическая сортировка - упорядочивание вершин ациклического ориентированного графа согласно частичному порядку, заданному ребрами орграфа на множестве его вершин.) +Топологическая сортировка может быть не единственной (например, если граф — пустой; или если есть три такие вершины a, b, c, что из a есть пути в b и в c, но ни из b в c, ни из c в b добраться нельзя).

+

Топологической сортировки может не существовать вовсе — если граф содержит циклы (поскольку при этом возникает противоречие: есть путь и из одной вершины в другую, и наоборот).

+
#include <vector>
+
+int n; // число вершин
+std::vector<std::vector<int>> graph; // граф
+std::vector<bool> visited;
+std::vector<int> $to$ pological_sort;
+
+void dfs (int vertex) {
+  visited[vertex] = true;
+  for (int u : graph[vertex])
+    if (!visited[u] == 0)
+      dfs(u);
+  $to$ pological_sort.push_back(vertex);
+}
+
+void $to$ pological_sort() {
+  visited.assign(n, false);
+  answer.clear();
+  for (int i = 0; i < n; ++i)
+    if (!visited[i])
+      dfs(i);
+  reverse(answer.begin(), answer.end());
+}
+
+

1.4. Нахождение компонент сильной связности. Алгоритм Косарайю с доказательством корректности(концепция). Алгоритм Тарьяна без доказательства корректности

+

Нахождение компонент сильной связности

+

Условие:

+

Дан ориентированный граф GG, множество вершин которого VV и множество рёбер — EE. Петли и кратные рёбра допускаются. Обозначим через nn количество вершин графа, через mm — количество рёбер.

+

Решение:

+

Напомним, что компонентой сильной связности (strongly connected component) называется такое (максимальное по включению) подмножество вершин $C4, что любые две вершины этого подмножества достижимы друг из друга, т.е. для любых $u, vv \in C: u \rightarrow v, vv \rightarrow u$, где символом \rightarrow здесь и далее мы будем обозначать достижимость, т.е. существование пути из первой вершины во вторую. Понятно, что компоненты сильной связности для данного графа не пересекаются, т.е. фактически это разбиение всех вершин графа. Отсюда логично определение конденсации G как графа, получаемого из данного графа сжатием каждой компоненты сильной связности в одну вершину. Каждой вершине графа конденсации соответствует компонента сильной связности графа G, а ориентированное ребро между двумя вершинами CiC_i и CjC_j графа конденсации проводится, если найдётся пара вершин u из CiC_i, из CjC_j, между которыми существовало ребро в исходном графе, т.е. (u,v)(u, v) из EE.

+

Важнейшим свойством графа конденсации является то, что он ацикличен. Действительно, предположим, что CCC \rightarrow C', докажем, что CCC' \mapsto C. Из определения конденсации получаем, что найдутся две вершины uu из CC и vv из CC', что uvu \rightarrow v. Доказывать будем от противного, т.е. предположим, что CCC' \rightarrow C, тогда найдутся две вершины uu' из CC и vv' из CC', что vuv' \rightarrow u'. Но т.к. uu и uu' находятся в одной компоненте сильной связности, то между ними есть путь; аналогично для vv и vv'. В итоге, объединяя пути, получаем, что vuv \rightarrow u, и одновременно uvu \rightarrow v. Следовательно, uu и vv должны принадлежать одной компоненте сильной связности, т.е. получили противоречие, что и требовалось доказать

+

Алгоритм Косарайю с доказательством корректности(концепция)

+

Алгоритм Косарайю предназначен для поиска компонент сильной связности в ориентированном графе и состоит из трёх шагов:

+
    +
  1. +

    Выполнить поиск в глубину (DFS), пока не будут «помечены» все вершины. Вершина считается «помеченной», когда ей присвоено время выхода из рекурсии (см. основные понятия).

    +
  2. +
  3. +

    Инвертировать исходный граф

    +
  4. +
  5. +

    Выполнить DFS в порядке убывания пометок вершин.

    +
  6. +
+

Полученные деревья каждого такта DFS последнего шага являются компонентами сильной связности

+

Доказательство корректности алгоритма:

+

Немного уточним, что требуется доказать: Вершины uu и vv сильно связаны \Leftrightarrow после выполнения алгоритма они принадлежат одному дереву такта DFS.

+

Если вершины u и vv были сильно связаны в графе GG, на третьем этапе будет найден путь из одной вершины в другую (по Лемме (Инвертирование рёбер цикла не влияет на его цикличность)), так как на первом шаге был найден путь uvu \rightarrow v, а на третьем - путь vuv \rightarrow u. Это означает, что по окончании алгоритма обе вершины лежат в одном дереве.

+

Вершины uu и vv лежат в одном и том же дереве поиска в глубину на третьем шаге алгоритма. Значит, они обе достижимы из корня rr этого дерева.

+

Вершина rr была рассмотрена на 3 шаге раньше всех, значит время выхода из неё на 1 шаге больше, чем время выхода из вершин uu и vv. Из этого мы получаем 2 случая:

+

Обе эти вершины были достижимы из rr в исходном графе. Это означает сильную связность вершин uu и rr и сильную связность вершин vv и rr (по Лемме 3). Склеивая пути мы получаем связность вершин uu и vv (по Лемме 1)

+

Хотя бы одна вершина не достижима из rr в исходном графе, например vv. Значит и rr была не достижима из vv в исходном графе, так как время выхода из rr — больше (если бы она была достижима, то время выхода из vv было бы больше, чем из rr, просмотрите ещё раз первый шаг примера). Значит между этими вершинами нет пути (ни в исходном, ни в инвертированном графах), но последнего быть не может, потому что по условию vv достижима из r на 3 шаге (в инвертированном графе)

+

Значит, из случая 1 и не существования случая 2 получаем, что вершины uu и vv сильно связаны в обоих графах

+

Алгоритм Тарьяна без доказательства корректности

+

Основой для алгоритма является структура данных "Система непересекающихся множеств", которая и была изобретена Тарьяном (Tarjan).

+

Алгоритм фактически представляет собой обход в глубину из корня дерева, в процессе которого постепенно находятся ответы на запросы. А именно, ответ на запрос (v,u)(v, u) находится, когда обход в глубину находится в вершине uu, а вершина vv уже была посещена, или наоборот.

+

Итак, пусть обход в глубину находится в вершине vv (и уже были выполнены переходы в её сыновей), и оказалось, что для какого-то запроса (v,u)(v, u) вершина u уже была посещена обходом в глубину. Научимся тогда находить LCALCA этих двух вершин.

+

Заметим, что LCA(v,u)LCA(v, u) является либо самой вершиной vv, либо одним из её предков. Получается, нам надо найти самую нижнюю вершину среди предков vv (включая её саму), для которой вершина u является потомком. Заметим, что при фиксированном vv по такому признаку (т.е. какой наименьший предок vv является и предком какой-то вершины) вершины дерева дерева распадаются на совокупность непересекающихся классов. Для каждого предка pvp \neq v вершины vv её класс содержит саму эту вершину, а также все поддеревья с корнями в тех её сыновьях, которые лежат "слева" от пути до vv (т.е. которые были обработаны ранее, чем была достигнута vv).

+

Нам надо научиться эффективно поддерживать все эти классы, для чего мы и применим структуру данных "Система непересекающихся множеств". Каждому классу будет соответствовать в этой структуре множество, причём для представителя этого множества мы определим величину ANCESTOR — ту вершину pp, которая и образует этот класс.

+

Рассмотрим подробно реализацию обхода в глубину. Пусть мы стоим в некоторой вершине vv. Поместим её в отдельный класс в структуре непересекающихся множеств, ANCESTOR[v] = vv. Как обычно в обходе в глубину, перебираем все исходящие рёбра $(v, toto )$. Для каждого такого toto мы сначала должны вызвать обход в глубину из этой вершины, а потом добавить эту вершину со всем её поддеревом в класс вершины vv. Это реализуется операцией Union структуры данных "система непересекающихся множеств", с последующей установкой ANCESTOR = vv для представителя множества (т.к. после объединения представитель класса мог измениться). Наконец, после обработки всех рёбер мы перебираем все запросы вида (v,u)(v, u), и если u была помечена как посещённая обходом в глубину, то ответом на этот запрос будет вершина LCA(v,u)LCA(v,u) = ANCESTOR[FindSet(u)]. Нетрудно заметить, что для каждого запроса это условие (что одна вершина запроса является текущей, а другая была посещена ранее) выполнится ровно один раз.

+

Оценим асимптотику. Она складывается из нескольких частей. Во-первых, это асимптотика обхода в глубину, которая в данном случае составляет O(n)O(n). Во-вторых, это операции по объединению множеств, которые в сумме для всех разумных n затрачивают O(n)O(n) операций. В-третьих, это для каждого запроса проверка условия (два раза на запрос) и определение результата (один раз на запрос), каждое, опять же, для всех разумных n выполняется за O(1)O(1). Итоговая асимптотика получается O(n+m)O(n + m), что означает для достаточно больших mm (n=O(m))(n = O(m)) ответ за O(1)O(1) на один запрос

+

1.5. Компоненты реберной двусвязности. Мосты. Поиск мостов

+

Компоненты реберной двусвязности

+
+

Компонентами рёберной двусвязности (англ. costal doubly-linked components) графа называют его подграфы, множества вершин которых - классы эквивалентности рёберной двусвязности, а множества рёбер - множества ребер из соответствующих классов эквивалентности.

+
+

Мосты

+
+

Мостом называется такое ребро, удаление которого делает граф несвязным (или, точнее, увеличивает число компонент связности).

+
+

Поиск мостов

+
    +
  • +

    Алгоритм: +Запустим обход в глубину из произвольной вершины графа; обозначим её через root. Заметим следующий факт (который несложно доказать):

    +

    Пусть мы находимся в обходе в глубину, просматривая сейчас все рёбра из вершины vv. Тогда, если текущее ребро (v,to)(v,to) таково, что из вершины toto и из любого её потомка в дереве обхода в глубину нет обратного ребра в вершину vv или какого-либо её предка, то это ребро является мостом. В противном случае оно мостом не является. (В самом деле, мы этим условием проверяем, нет ли другого пути из vv в toto , кроме как спуск по ребру (v,to)(v,to) дерева обхода в глубину.) +Теперь осталось научиться проверять этот факт для каждой вершины эффективно. Для этого воспользуемся "временами входа в вершину", вычисляемыми алгоритмом поиска в глубину.

    +

    Итак, пусть tin[v]tin[v] — это время захода поиска в глубину в вершину vv. Теперь введём массив fup[v]fup[v], который и позволит нам отвечать на вышеописанные запросы. Время fup[v]fup[v] равно минимуму из времени захода в саму вершину tin[v]tin[v], времён захода в каждую вершину pp, являющуюся концом некоторого обратного ребра (v,p)(v, p), а также из всех значений fup[to]fup[to] для каждой вершины toto , являющейся непосредственным сыном vv в дереве поиска:

    +

    fup[v]=min(tin[v],tin[p],fup(to))fup[v] = \min(tin[v], tin[p], fup(to)) (v,p)(v, p) - back edge, (v,to)(v, to) - tree edge (здесь "back edge" — обратное ребро, "tree edge" — ребро дерева)

    +

    Тогда, из вершины vv или её потомка есть обратное ребро в её предка тогда и только тогда, когда найдётся такой сын toto , что fup[to]tin[v]fup[to] \leq tin[v]. (Если fup[to]=tin[v]fup[to] = tin[v], то это означает, что найдётся обратное ребро, приходящее точно в vv; если же fup[to]<tin[v]fup[to] < tin[v], то это означает наличие обратного ребра в какого-либо предка вершины vv.)

    +

    Таким образом, если для текущего ребра (v,to)(v,to) (принадлежащего дереву поиска) выполняется fup[to]>tin[v]fup[to] > tin[v], то это ребро является мостом; в противном случае оно мостом не является.

    +
  • +
+

1.6. Компоненты вершинной двусвязности. Точки сочленения. Поиск точек сочленения

+

Компоненты вершинной двусвязности

+
+

Компонентами вершинной двусвязности графа, называют его подграфы, множества ребер которых — классы эквивалентности вершинной двусвязности, а множества вершин — множества всевозможных концов ребер из соответствующих классов.

+
+

Точки сочленения

+
+

Точкой сочленения называется вершина, при удалении которой связный неориентированный граф становится несвязным.

+
+

Поиск точек сочленения

+
    +
  • +

    Алгоритм: +Запустим обход в глубину из произвольной вершины графа; обозначим её через rootroot. Заметим следующий факт (который несложно доказать):

    +

    Пусть мы находимся в обходе в глубину, просматривая сейчас все рёбра из вершины vrootv \neq root. Тогда, если текущее ребро (v,to)(v, to) таково, что из вершины toto и из любого её потомка в дереве обхода в глубину нет обратного ребра в какого-либо предка вершины v, то вершина vv является точкой сочленения. В противном случае, т.е. если обход в глубину просмотрел все рёбра из вершины vv, и не нашёл удовлетворяющего вышеописанным условиям ребра, то вершина vv не является точкой сочленения. (В самом деле, мы этим условием проверяем, нет ли другого пути из vv в toto ) +Рассмотрим теперь оставшийся случай: v=rootv = root. Тогда эта вершина является точкой сочленения тогда и только тогда, когда эта вершина имеет более одного сына в дереве обхода в глубину. (В самом деле, это означает, что, пройдя из rootroot по произвольному ребру, мы не смогли обойти весь граф, откуда сразу следует, что rootroot — точка сочленения). +(Ср. формулировку этого критерия с формулировкой критерия для алгоритма поиска мостов.)

    +

    Теперь осталось научиться проверять этот факт для каждой вершины эффективно. Для этого воспользуемся "временами входа в вершину", вычисляемыми алгоритмом поиска в глубину.

    +

    Итак, пусть tin[v]tin[v] — это время захода поиска в глубину в вершину v. Теперь введём массив fup[v]fup[v], который и позволит нам отвечать на вышеописанные запросы. Время fup[v]fup[v] равно минимуму из времени захода в саму вершину tin[v]tin[v], времён захода в каждую вершину p, являющуюся концом некоторого обратного ребра (v,p)(v, p), а также из всех значений fup[to]fup[to] для каждой вершины toto , являющейся непосредственным сыном vv в дереве поиска:

    +

    fup[v]=min(tin[v],tin[p],fup(to))fup[v] = \min(tin[v], tin[p], fup(to)) (v,p)(v, p) - back edge, (v,to)(v, to) - tree edge (здесь "back edge" — обратное ребро, "tree edge" — ребро дерева)

    +

    Тогда, из вершины vv или её потомка есть обратное ребро в её предка тогда и только тогда, когда найдётся такой сын toto , что fup[to]<tin[v]fup[to] < tin[v].

    +

    Таким образом, если для текущего ребра (v,to)(v,to) (принадлежащего дереву поиска) выполняется fup[to]tin[v]fup[to] \geq tin[v], то вершина vv является точкой сочленения. Для начальной вершины v=rootv = root критерий другой: для этой вершины надо посчитать число непосредственных сыновей в дереве обхода в глубину.

    +
  • +
+

1.7. Волновой алгоритм. Обход в ширину (применение очереди в волновом алгоритме)

+

Волновой алгоритм

+
+

Алгоритм волновой трассировки (волновой алгоритм) — алгоритм поиска пути, алгоритм поиска кратчайшего пути на планарном графе. Принадлежит к алгоритмам, основанным на методах поиска в ширину.

+
+

Алгоритм предназначен для поиска кратчайшего пути от стартовой ячейки к конечной ячейке, если это возможно, либо, при отсутствии пути, выдать сообщение о непроходимости.

+

Работа алгоритма включает в себя три этапа: инициализацию, распространение волны и восстановление пути.

+

Инициализация

+
Пометить стартовую ячейку 
+d := 0 
+
+

Распространение волны

+
ЦИКЛ
+  ДЛЯ каждой ячейки loc, помеченной числом d
+    пометить все соседние свободные непомеченные ячейки числом d + 1
+  КЦ
+  d := d + 1
+ПОКА (финишная ячейка не помечена) И (есть возможность распространения волны) 
+
+

Восстановление пути

+
ЕСЛИ финишная ячейка помечена
+ТО
+  перейти в финишную ячейку
+  ЦИКЛ
+    выбрать среди соседних ячейку, помеченную числом на 1 меньше числа в текущей ячейке
+    перейти в выбранную ячейку и добавить её к пути
+  ПОКА текущая ячейка — не стартовая
+  ВОЗВРАТ путь найден
+ИНАЧЕ
+  ВОЗВРАТ путь не найден
+
+

Обход в ширину (применение очереди в волновом алгоритме)

+
+

Поиск в ширину (англ. breadth-first search) — один из основных алгоритмов на графах, позволяющий находить все кратчайшие пути от заданной вершины и решать многие другие задачи.

+
+
    +
  • +

    Описание алгоритма +На вход алгоритма подаётся невзвешенный граф и номер стартовой вершины ss. Граф может быть как ориентированным, так и неориентированным — для алгоритма это не важно.

    +

    Основную идею алгоритма можно понимать как процесс «поджигания» графа: на нулевом шаге мы поджигаем вершину ss, а на каждом следующем шаге огонь с каждой уже горящей вершины перекидывается на всех её соседей, в конечном счете поджигая весь граф.

    +

    Если моделировать этот процесс, то за каждую итерацию алгоритма будет происходить расширение «кольца огня» в ширину на единицу. Номер шага, на котором вершина vv начинает гореть, в точности равен длине её минимального пути из вершины s.

    +
  • +
+
std::vector<int> graph;
+
+void bfs(int s) {
+    std::queue<int> queue;
+    queue.push(s);
+    
+    std::vector<int> depth(n, -1), parent(n);
+    depth[s] = 0;
+    
+    while (!queue.empty()) {
+        int vertex = queue.front();
+        queue.pop();
+        for (int u : g[vertex]) {
+            if (depth[u] == -1) {
+                queue.push(u);
+                depth[u] = depth[vertex] + 1;
+                parent[u] = vertex;
+            }
+        }
+    }
+} 
+
+

Теперь, чтобы восстановить кратчайший путь до какой-то вершины v, это можно сделать через массив p:

+
while (v != s) {
+    std::cout << v << endl;
+    v = p[v];
+}
+
+

Обратим внимание, что путь выведется в обратном порядке.

+

1.8. Критерий существования Эйлерова пути и цикла в ориентированном и неориентированном графе. Поиск эйлерова пути и цикла

+

Критерий существования Эйлерова пути и цикла в неориентированном графе

+
    +
  • +

    Согласно теореме, доказанной Эйлером, эйлеров цикл существует тогда и только тогда, когда

    +
      +
    1. Граф связный или будет являться связным, если удалить из него все изолированные вершины.
    2. +
    3. В нём отсутствуют вершины нечётной степени.
    4. +
    +
  • +
  • +

    Эйлеров путь в графе существует тогда и только тогда, когда

    +
      +
    1. Граф связный
    2. +
    3. Содержит не более двух вершин нечётной степени. Ввиду леммы о рукопожатиях, число вершин с нечётной степенью должно быть чётным. А значит эйлеров путь существует только тогда, когда это число равно нулю или двум. Причём, когда оно равно нулю, эйлеров путь вырождается в эйлеров цикл.
    4. +
    +
  • +
+

Критерий существования Эйлерова пути и цикла в ориентированном графе

+
    +
  • В ориентированном графе G=(V,E)G = (V, E) существует эйлеров цикл тогда и только тогда, когда: +
      +
    1. Входная степень любой вершины равна ее выходной степени.
    2. +
    3. Все компоненты слабой связности кроме, может быть одной, не содержат ребер.
    4. +
    +
  • +
  • В ориентированном графе G=(V,E)G = (V, E) существует эйлеров путь если: +
      +
    1. Входная степень любой вершины равна ее выходной степени, кроме двух вершин графа, для одной из которых (deg+) − (deg−) = 1, а для другой (deg+) − (deg−) = −1 .
    2. +
    3. Все компоненты слабой связности кроме, может быть одной, не содержат ребер.
    4. +
    +
  • +
+

Поиск эйлерова пути и цикла

+
    +
  • +

    Поиск эйлерова пути в графе +Можно всегда свести задачу поиска эйлерова пути к задаче поиска эйлерова цикла. Действительно, предположим, что эйлерова цикла не существует, а эйлеров путь существует. Тогда в графе будет ровно 2 вершины нечётной степени. Соединим эти вершины ребром, и получим граф, в котором все вершины чётной степени, и эйлеров цикл в нём существует. Найдём в этом графе эйлеров цикл (алгоритмом, описанным ниже), а затем удалим из ответа несуществующее ребро.

    +
  • +
  • +

    Поиск эйлерова цикла в графе +Будем рассматривать самый общий случай — случай ориентированного мультиграфа, возможно, с петлями. Также мы предполагаем, что эйлеров цикл в графе существует (и состоит хотя бы из одной вершины). Для поиска эйлерова цикла воспользуемся тем, что эйлеров цикл — это объединение всех простых циклов графа. Следовательно, наша задача — эффективно найти все циклы и эффективно объединить их в один.

    +

    Реализовать это можно, например, так, рекурсивно:

    +
    procedure find_all_cycles (v)
    +var массив cycles
    +1. пока есть цикл, проходящий через v, находим его
    +    добавляем все вершины найденного цикла в массив cycles (сохраняя порядок обхода)
    +    удаляем цикл из графа
    +2. идем по элементам массива cycles
    +    каждый элемент cycles[i] добавляем к ответу
    +    из каждого элемента рекурсивно вызываем себя: find_all_cycles (cycles[i])
    +
    +

    Достаточно вызвать эту процедуру из любой вершины графа, и она найдёт все циклы в графе, удалит их из графа и объединит их в один эйлеров цикл. +Для поиска цикла на шаге 1 используем поиск в глубину. +Сложность полученного алгоритма — O(E)O(|E|), то есть линейная относительно количества рёбер в данном графе

    +
  • +
+

2. Планарность графа

+

2.1. Формула Эйлера. Теорема Портнягина-Куратовского. Теорема Вагнера

+

Формула Эйлера

+

Для произвольного плоского связного графа GG с VV вершинами, EE ребрами и FF гранями справедливо следующее соотношение: VE+F=2V − E + F = 2.

+

Теорема Портнягина-Куратовского

+

Теорема Понтрягина — Куратовского, или теорема Куратовского, — теорема в теории графов, дающая необходимое и достаточное условие планарности графа. Имеет эквивалентную формулировку на языке миноров, так называемою теорему Вагнера.

+

Теорема утверждает, что графы K5K_5 (полный граф на 5 вершинах) и K3,3K_{3,3} (полный двудольный граф имеющий по 3 вершины в каждой доле) являются единственными минимальными непланарными графами.

+

Теорема Вагнера

+

Теорема Вагнера — характеризация планарных графов тесно связанная с теоремой Понтрягина — Куратовского.

+

Теорема утверждает, что конечный граф является планарным тогда и только тогда, когда его миноры не включают ни K5K_5 (полный граф с пятью вершинами), ни K3,3K_{3,3} (коммунальный граф, полный двудольный граф с тремя вершинами в каждой доле).

+

2.2. Гамма алгоритм. Контактная вершина

+

Гамма алгоритм

+
    +
  • Чтобы проверить планарность графа и произвести его плоскую укладку, удобно пользоваться гамма-алгоритмом.
  • +
  • Определения: +
      +
    1. Сегмент графа ГГ относительно подграфа HH — компонента связности графа GHG \setminus H.
    2. +
    3. Допустимая грань сегмента — грань графа HH, содержащая все контактные вершины.
    4. +
    5. Контактная вершина сегмента SS графа GG подграфа HH — любая вершина в SS и в HH.
    6. +
    7. Гамма-цепь сегмента — любая цепь без повторов вершин, содержащих ровно две контактные вершины — начало и конец.
    8. +
    +
  • +
  • Алгоритм: +
      +
    1. Уложить на плоскость любой цикл HH графа GG без повторов вершин.
    2. +
    3. Построить все сегменты S1S_1 , ...... , SkS_k графа GG по HH.
    4. +
    5. Если есть сегмент SiS_i c одной допустимой гранью — выбрать его.
    6. +
    7. Если все сегменты имеют несколько дополнительных граней — выбрать любой.
    8. +
    9. Выбрать произвольную гамма-цепь сегмента и уложить её в допустимую грань.
    10. +
    11. Перейти к шагу (2), добавив гамма-цепь к графу HH.
    12. +
    +
  • +
+

2.3. Теорема о корректности Гамма алгоритма. Асимптотика алгоритма

+

Теорема о корректности Гамма алгоритма

+
    +
  • +

    Теорема: Гамма-алгоритм корректен, то есть если GG — планарный граф, то результатом каждого шага гамма-алгоритма является частичная укладка GG'.

    +
  • +
  • +

    Доказательство: +Докажем индукцией по числу шагов.

    +

    База индукции: полученный на этапе инициализации граф G0G_0 является простым циклом, он будет присутствовать в любой укладке графа GG. Таким образом, G0G_0 является частичной укладкой.

    +

    Шаг индукции: пусть граф Gk1G_{k−1}, полученный на k1k−1-м шаге работы алгоритма, является частичной укладкой. Докажем, что граф Gk=Gk1LkG_k = G_{k−1} \cup L_k, полученный на kk-м шаге присоединением цепи LkL_k, также является частичной укладкой.

    +

    Заметим, что на текущем шаге нет такого сегмента SS относительно Gk1G_{k−1}, для которого бы выполнялось равенство Γ(S)=Γ(S) = \varnothing, так как в противном случае существовала бы цепь этого сегмента, контактные вершины которой принадлежали бы разным граням и укладка которой была бы невозможна. Следовательно, нельзя было бы уложить SS,что противоречит тому, что GG — планарный граф. Значит, мы можем рассматривать только следующие два случая:

    +
      +
    1. +

      Существует сегмент SS для которого есть единственная вмещающая грань ΓΓ, то есть Γ(S)=1|Γ(S)| = 1. Так как только грани ΓΓ принадлежат все контактные вершины SS, то укладка этого сегмента в эту грань неизбежна. Это значит, что помещая любую цепь LSL \subset S, снова получим частичную укладку графа.

      +
    2. +
    3. +

      Для любого сегмента SS Γ(S)2|Γ(S)| \geq 2. Построим граф A(Gk1)A(G'k−1), который по лемме 2 является двудольным. Рассмотрим его связную компоненту KK, которая содержит не менее двух вершин. Граф KK также является двудольным. По лемме 1 для любого сегмента SKS \in K справедливо Γ(S)=Γ1,Γ2Γ(S)={Γ1, Γ2}. Так как граф KK двудольный, то мы можем по очереди помещать сегменты KK в разные грани, причем конфликтующих сегментов не возникнет в силу четности всех циклов в графе. Результатом будет частичная укладка графа.

      +
    4. +
    +

    Таким образом, на каждом шаге мы получаем частичную укладку графа, что доказывает корректность гамма-алгоритма.

    +
  • +
+

Асимптотика алгоритма - O(n3)O(n^3).

+

3. Кратчайшие пути во взвешенном графе

+

3.1. Алгоритм Дейкстры. Доказательство корректности(с доказательством). Оценка времени работы. Дерево кратчайших путей

+

Алгоритм Дейкстры

+

Алгоритм Дейкстры (англ. Dijkstra’s algorithm) — алгоритм на графах, изобретённый нидерландским учёным Эдсгером Дейкстрой в 1959 году. Находит кратчайшие пути от одной из вершин графа до всех остальных. Алгоритм работает только для графов без рёбер отрицательного веса.

+
func dijkstra(s):
+    for $v \in V$
+        d[v] = $ \infinity $
+        used[v] = false
+    d[s] = 0
+    for $i \in V$
+        v = null
+        for $j \in V$    // найдём вершину с минимальным расстоянием
+            if !used[j] and (v == null or d[j] < d[v])
+                v = j
+        if d[v] == $\infinity$
+            break
+        used[v] = true
+        for e : исходящие из v рёбра     // произведём релаксацию по всем рёбрам, исходящим из v
+            if d[v] + e.len < d[e.to]
+                d[e.to] = d[v] + e.len
+
+

Доказательство корректности

+
    +
  • Пусть G=(V,E)G=(V,E) — ориентированный взвешенный граф, вес рёбер которого неотрицателен, s — стартовая вершина. Тогда после выполнения алгоритма Дейкстры d(u)=ρ(s,u)d(u)=\rho(s, u) для всех uu, где ρ(s,u)\rho (s,u) — длина кратчайшего пути из вершины ss в вершину uu.
  • +
+

Докажем по индукции, что в момент посещения любой вершины uu, d(u)=ρ(s,u)d(u)= \rho (s,u).

+

На первом шаге выбирается ss, для неё выполнено: d(s)=ρ(s,s)=0d(s) = \rho (s,s) = 0. Пусть для nn первых шагов алгоритм сработал верно и на n+1n+1 шагу выбрана вершина uu. Докажем, что в этот момент d(u)=ρ(s,u)d(u)= \rho (s,u). Для начала отметим, что для любой вершины vv, всегда выполняется d(v)ρ(s,v)d(v) \geq \rho (s,v) (алгоритм не может найти путь короче, чем кратчайший из всех существующих). Пусть PP — кратчайший путь из ss в uu, vv — первая непосещённая вершина на PP, zz — предшествующая ей (следовательно, посещённая). Поскольку путь PP кратчайший, его часть, ведущая из ss через zz в vv, тоже кратчайшая, следовательно ρ(s,v)=ρ(s,z)+w(zv)\rho(s,v)= \rho(s,z)+w(zv). По предположению индукции, в момент посещения вершины zz выполнялось d(z)=ρ(s,z)d(z)= \rho (s,z), следовательно, вершина vv тогда получила метку не больше чем d(z)+w(zv)=ρ(s,z)+w(zv)=ρ(s,v)d(z) + w(zv) = \rho (s,z) + w(zv) = \rho(s,v), следовательно, d(v)=ρ(s,v)d(v)=\rho(s,v). С другой стороны, поскольку сейчас мы выбрали вершину uu, её метка минимальна среди непосещённых, то есть d(u)d(v)=ρ(s,v)ρ(s,u)d(u) \leq d(v) = \rho (s,v) \leq \rho (s,u), где второе неравенсто верно из-за ранее упомянутого определения вершины vv в качестве первой непосещённой вершины на PP, то есть вес пути до промежуточной вершины не превосходит веса пути до конечной вершины вследствие неотрицательности весовой функции. Комбинируя это с d(u)ρ(s,u)d(u) \geq \rho(s,u), имеем d(u)=ρ(s,u)d(u)=\rho(s,u), что и требовалось доказать. Поскольку алгоритм заканчивает работу, когда все вершины посещены, в этот момент d(u)=ρ(s,u)d(u)= \rho (s,u) для всех uu.

+

Оценка времени работы

+
    +
  • Если m=n2m = n ^ 2(граф плотный), то O(n2)O(n^2).
  • +
  • Если m=nm = n(граф разреженный), то O(mlogn)O(m log n).
  • +
+

Дерево кратчайших путей

+

Решение ряда прикладных задач сводится к нахождению дерева кратчайших путей с корнем в заданной вершине ss. Такое дерево дает кратчайшие (s,v)(s, v)-пути от заданной вершины ss до любой вершины vv орграфа G=(V,Е)G = (V, Е). Если веса всех дуг орграфа GG неотрицательны, то дерево кратчайших путей можно построить с помощью алгоритма Дейкстры.

+

3.2. Потенциалы. Условие применимости алгоритма Дейкстры для измененных длин ребер. Потенциал π(v)=ρ(v,t)\pi(v) = \rho(v, t)

+

Потенциалы

+

Пусть дана транспортная сеть G(V,E)G(V,E), где VV — множество вершин графа, а EE — множество рёбер. Введем в каждой вершине потенциал π(v)\pi(v). Тогда потенциальный вес (то есть стоимость) ребра (u,v)(u,v) определяется как wp(u,v)=w(u,v)+π(u)π(v)w_p(u,v)=w(u,v)+\pi(u)−\pi(v)

+

Условие применимости алгоритма Дейкстры для измененных длин ребер

+

Можно применять, когда нет отрицательных циклов (следует из курса дискретной математики 1 курс 2 семестр)

+

Потенциал π(v)=ρ(v,t)\pi(v) = \rho(v, t)

+

3.3. Алгоритм A*. Условие монотонности на эвристику. Примеры эвристик

+

Алгоритм A*

+

Алгоритм A* - алгоритм поиска по первому наилучшему совпадению на графе, который находит маршрут с наименьшей стоимостью от одной вершины (начальной) к другой (целевой, конечной).

+

Порядок обхода вершин определяется эвристической функцией «расстояние + стоимость» (обычно обозначаемой как f(x)f(x)). Эта функция — сумма двух других: функции стоимости достижения рассматриваемой вершины (x) из начальной (обычно обозначается как g(x)g(x) и может быть как эвристической, так и нет), и функции эвристической оценки расстояния от рассматриваемой вершины к конечной (обозначается как h(x)h(x)).

+
    +
  1. Функция h(x)h(x) должна быть допустимой эвристической оценкой, то есть не должна переоценивать расстояния к целевой вершине. Например, для задачи маршрутизации h(x)h(x) может представлять собой расстояние до цели по прямой линии, так как это физически наименьшее возможное расстояние между двумя точками.
  2. +
  3. Более сильное условие — функция h(v)h(v) должна быть монотонной. (Эвристическая функция h(v)h(v) называетя монотонной (или преемственной), если для любой вершины v1v_1 и ее потомка v2v_2 разность h(v1)h(v_1) и h(v2)h(v_2) не превышает фактического веса ребра c(v1,v2)c(v_1, v_2) от v1v_1 до v2v_2, а эвристическая оценка целевого состояния равна нулю.)
  4. +
+
function A*(start, goal, f)
+     % множество уже пройденных вершин
+     var closed := the empty set
+     % множество частных решений
+     var open := make_queue(f)
+     enqueue(open, path(start))
+     while open is not empty
+         var p := remove_first(open)
+         var x := the last node of p
+         if x in closed
+            continue
+         if x = goal
+            return $\rho$
+         add(closed, x)
+         % добавляем смежные вершины
+         foreach y in successors(x)
+            enqueue(open, add_to_path(p, y))
+     return failure
+
+

Примеры эвристик

+
    +
  • Если мы можем перемещаться в четырех направлениях, то в качестве эвристики стоит выбрать манхэттенское расстояние h(v)=v.xgoal.x+v.ygoal.yh(v)=|v.x−goal.x|+|v.y−goal.y|.
  • +
  • Расстояние Чебышева применяется, когда к четырем направлениям добавляются диагонали: h(v)=max(v.xgoal.x,v.ygoal.y)h(v)=max(|v.x−goal.x|,|v.y−goal.y|).
  • +
  • Если передвижение не ограничено сеткой, то можно использовать евклидово расстояние по прямой: h(v)=((v.xgoal.x)2+(v.ygoal.y)2)1/2h(v)=((v.x−goal.x) ^ 2+(v.y−goal.y)^2) ^ {1/2}.
  • +
+

3.4. Алгоритм Форда-Беллмана. Хранение в матрице: DvkD_{vk} равно длине кратчайшего пути до вершины vv за ровно kk ребер (не более kk ребер). Доказательство корректности (полное). Оценка времени работы

+

Количество путей длины k рёбер можно найти с помощью метода динамического программирования. Пусть d[k][u]d[k][u] — количество путей длины k рёбер, заканчивающихся в вершине u. Тогда d[k][u]=Σv:vuE(d[k1][v])d[k][u]= \Sigma v: v u \in E(d[k−1][v]).

+

Аналогично посчитаем пути кратчайшей длины. Пусть ss — стартовая вершина. Тогда d[k][u]=minv:vuE(d[k1][v]+w(u,v))d[k][u]=\min v: v u \in E(d[k−1][v]+w(u,v)), при этом d[0][s]=0d[0][s]=0, а d[0][u]=+d[0][u]=+\infty

+

Если существует кратчайший путь от s до t, то ρ(s,t)=mind[k][t](k=0..n1)\rho(s,t)=\min d[k][t](k=0..n−1)

+

Пусть кратчайший путь состоит из kk ребер, тогда корректность формулы следует из динамики, приведенной ниже.

+
for k = 0 to |V|−2       // вершины нумеруются с единицы
+    for v∈V
+       for (u,v)∈E
+          d[k + 1][v] = min(d[k + 1][v], d[k][u] + ω(u,v))     // ω(u,v) — вес ребра uv
+
+
bool fordBellman(s):
+    for v∈V
+        d[v] = 1
+    d[s] = 0
+    for i = 0 to |V|−1
+        for (u,v)∈E
+            if d[v] > d[u] + ω(u,v)     // ω(u,v) — вес ребра uv
+                d[v] = d[u] + ω(u,v)
+    for (u,v)∈E
+        if d[v] > d[u] + ω(u,v)
+            return false
+    return true
+
+

Доказательство корректности(полное)

+

Во-первых, сразу заметим, что для недостижимых из vv вершин алгоритм отработает корректно: для них метка d[]d[] так и останется равной бесконечности (т.к. алгоритм Форда-Беллмана найдёт какие-то пути до всех достижимых из ss вершин, а релаксация во всех остальных вершинах не произойдёт ни разу).

+

Докажем теперь следующее утверждение: после выполнения ii фаз алгоритм Форда-Беллмана корректно находит все кратчайшие пути, длина которых (по числу рёбер) не превосходит ii.

+

Иными словами, для любой вершины aa обозначим через kk число рёбер в кратчайшем пути до неё (если таких путей несколько, можно взять любой). Тогда это утверждение говорит о том, что после kk фаз этот кратчайший путь будет найден гарантированно.

+

Доказательство.

+

Рассмотрим произвольную вершину a, до которой существует путь из стартовой вершины v, и рассмотрим кратчайший путь до неё: (p0=v,p1,...,pk=a)(p_0=v, p_1, ..., p_k=a). Перед первой фазой кратчайший путь до вершины p0=vp_0=v найден корректно. Во время первой фазы ребро (p0,p1)(p_0,p_1) было просмотрено алгоритмом Форда-Беллмана, следовательно, расстояние до вершины p1p_1 было корректно посчитано после первой фазы. Повторяя эти утверждения k раз, получаем, что после kk-й фазы расстояние до вершины pk=ap_k=a посчитано корректно, что и требовалось доказать.

+

Последнее, что надо заметить — это то, что любой кратчайший путь не может иметь более n1n-1 ребра. Следовательно, алгоритму достаточно произвести только n1n-1 фазу. После этого ни одна релаксация гарантированно не может завершиться улучшением расстояния до какой-то вершины.

+

Оценка времени работы

+

Инициализация занимает θ(V)\theta(V) времени, каждый из V1|V|−1 проходов требует θ(E)\theta(E) времени, обход по всем ребрам для проверки наличия отрицательного цикла занимает O(E)O(E) времени. Значит алгоритм Беллмана-Форда работает за O(VE)O(VE) времени.

+

3.5. Восстановление пути. Детектирование цикла отрицательного веса. Поиск самого цикла

+

Восстановление пути

+
void solve() {
+  vector<int> d (n, INF);
+  d[v] = 0;
+  vector<int> p (n, -1);
+  for (;;) {
+    bool any = false;
+    for (int j=0; j<m; ++j)
+      if (d[e[j].a] < INF)
+        if (d[e[j].b] > d[e[j].a] + e[j].cost) {
+          d[e[j].b] = d[e[j].a] + e[j].cost;
+          p[e[j].b] = e[j].a;
+          any = true;
+        }
+    if (!any)  break;
+  }
+ 
+  if (d[t] == INF)
+    cout << "No path from " << v << " to " << t << ".";
+  else {
+    vector<int> path;
+    for (int cur=t; cur!=-1; cur=p[cur])
+      path.push_back (cur);
+    reverse (path.begin(), path.end());
+ 
+    cout << "Path from " << v << " to " << t << ": ";
+    for (size_t i=0; i<path.size(); ++i)
+      cout << path[i] << ' ';
+  }
+}
+
+

Здесь мы сначала проходимся по предкам, начиная с вершины t, и сохраняем весь пройденный путь в списке path\rm path. В этом списке получается кратчайший путь от v до t, но в обратном порядке, поэтому мы вызываем reverse\rm reverse от него и затем выводим.

+

Детектирование цикла отрицательного веса

+

Пусть G=(V,E)G=(V,E) — взвешенный ориентированный граф, ss — стартовая вершина. Если граф GG не содержит отрицательных циклов, достижимых из вершины ss, то алгоритм возвращает true\rm true и для всех vVv \in V d[v]=δ(s,v)d[v]= \delta (s,v). Если граф GG содержит отрицательные циклы, достижимые из вершины ss, то алгоритм возвращает false\rm false.

+

Поиск самого цикла

+

Приведенная выше реализация позволяет определить наличие в графе цикла отрицательного веса. Чтобы найти сам цикл, достаточно хранить вершины, из которых производится релаксация.

+

Если после V1|V|−1 итерации найдется вершина vv, расстояние до которой можно уменьшить, то эта вершина либо лежит на каком-нибудь цикле отрицательного веса, либо достижима из него. Чтобы найти вершину, которая лежит на цикле, можно V1|V|−1 раз пройти назад по предкам из вершины vv. Так как наибольшая длина пути в графе из V|V| вершин равна V1|V|−1, то полученная вершина u будет гарантированно лежать на отрицательном цикле.

+

Зная, что вершина u лежит на цикле отрицательного веса, можно восстанавливать путь по сохраненным вершинам до тех пор, пока не встретится та же вершина u. Это обязательно произойдет, так как в цикле отрицательного веса релаксации происходят по кругу.

+

3.6. Нахождение кратчайших путей с учетом циклов отрицательного веса

+

Заметим, что updated_vertices на V|V|-м шаге содержит два типа вершин: те, которые лежат на цикле отрицательного веса и те, которые сами достижимы из какого-то цикла отрицательного веса. При этом из доказательства леммы выше мы знаем, что от каждого (!) цикла отрицательного веса в множестве будет хотя бы по одному представителю. Из этих двух наблюдений следует корректность и полнота следующего алгоритма:

+

Запускаем Форда-Беллмана на V|V| шагов. Если на последнем шаге до вершины расстояние не поменялось, то оно и кратчайшее. Если поменялось, то до неё и до всех вершин, достижимых из неё, расстояние от стартовой вершины равно минус бесконечности. Чтобы найти все вершины с расстоянием минус бесконечность, достаточно запустить dfs из всех вершин, расстояние до которых уменьшилось на последней итерации алгоритма.

+

3.7. Алгоритм Флойда. Доказательство (концепция). Восстановление пути

+

Алгоритм Флойда

+
+

Алгоритм Флойда (алгоритм Флойда–Уоршелла) — алгоритм нахождения длин кратчайших путей между всеми парами вершин во взвешенном ориентированном графе. Работает корректно, если в графе нет циклов отрицательной величины, а в случае, когда такой цикл есть, позволяет найти хотя бы один такой цикл.

+
+

Ключевая идея алгоритма — разбиение процесса поиска кратчайших путей на фазы.

+

Перед kk-ой фазой (k=1...n)(k = 1 ... n) считается, что в матрице расстояний d[][]d[][] сохранены длины таких кратчайших путей, которые содержат в качестве внутренних вершин только вершины из множества 1,2,...,k1{1, 2, ..., k-1} (вершины графа мы нумеруем, начиная с единицы).

+

Иными словами, перед kk-ой фазой величина d[i][j]d[i][j] равна длине кратчайшего пути из вершины ii в вершину jj, если этому пути разрешается заходить только в вершины с номерами, меньшими k (начало и конец пути не считаются).

+

Легко убедиться, что чтобы это свойство выполнилось для первой фазы, достаточно в матрицу расстояний d[][]d[][] записать матрицу смежности графа: d[i][j]=g[i][j]d[i][j] = g[i][j] — стоимости ребра из вершины ii в вершину jj. При этом, если между какими-то вершинами ребра нет, то записать следует величину "бесконечность". Из вершины в саму себя всегда следует записывать величину 0, это критично для алгоритма.

+

Пусть теперь мы находимся на kk-ой фазе, и хотим пересчитать матрицу d[][]d[][] таким образом, чтобы она соответствовала требованиям уже для k+1k+1-ой фазы. Зафиксируем какие-то вершины ii и jj. У нас возникает два принципиально разных случая:

+

Кратчайший путь из вершины ii в вершину jj, которому разрешено дополнительно проходить через вершины 1,2,...,k{1, 2, ..., k}, совпадает с кратчайшим путём, которому разрешено проходить через вершины множества 1,2,...,k1{1, 2, ..., k-1}. +В этом случае величина d[i][j]d[i][j] не изменится при переходе с kk-ой на k+1k+1-ую фазу.

+

"Новый" кратчайший путь стал лучше "старого" пути. Это означает, что "новый" кратчайший путь проходит через вершину kk. Сразу отметим, что мы не потеряем общности, рассматривая далее только простые пути (т.е. пути, не проходящие по какой-то вершине дважды).

+

Тогда заметим, что если мы разобьём этот "новый" путь вершиной kk на две половинки (одна идущая iki \rightarrow k, а другая — kjk \rightarrow j), то каждая из этих половинок уже не заходит в вершину kk. Но тогда получается, что длина каждой из этих половинок была посчитана ещё на k1k-1-ой фазе или ещё раньше, и нам достаточно взять просто сумму d[i][k]+d[k][j]d[i][k] + d[k][j], она и даст длину "нового" кратчайшего пути.

+

Объединяя эти два случая, получаем, что на kk-ой фазе требуется пересчитать длины кратчайших путей между всеми парами вершин ii и jj следующим образом:

+
new_d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
+
+

Таким образом, вся работа, которую требуется произвести на kk-ой фазе — это перебрать все пары вершин и пересчитать длину кратчайшего пути между ними. В результате после выполнения nn-ой фазы в матрице расстояний d[i][j]d[i][j] будет записана длина кратчайшего пути между ii и jj, либо бесконечность, если пути между этими вершинами не существует.

+

Последнее замечание, которое следует сделать, — то, что можно не создавать отдельную матрицу newd[][]new_d[][] для временной матрицы кратчайших путей на kk-ой фазе: все изменения можно делать сразу в матрице d[][]d[][]. В самом деле, если мы улучшили (уменьшили) какое-то значение в матрице расстояний, мы не могли ухудшить тем самым длину кратчайшего пути для каких-то других пар вершин, обработанных позднее.

+

Доказательство (концепция)

+

Доказательство "правильности" работы этого алгоритма также очевидно и выполняется с помощью математической индукции по kk, показывая, что на kk-й итерации вершина kk включается в путь только тогда, когда новый путь короче старого.

+

Восстановление пути

+

Легко поддерживать дополнительную информацию — так называемых "предков", по которым можно будет восстанавливать сам кратчайший путь между любыми двумя заданными вершинами в виде последовательности вершин.

+

Для этого достаточно кроме матрицы расстояний d[][]d[][] поддерживать также матрицу предков p[][]p[][], которая для каждой пары вершин будет содержать номер фазы, на которой было получено кратчайшее расстояние между ними. Понятно, что этот номер фазы является не чем иным, как "средней" вершиной искомого кратчайшего пути, и теперь нам просто надо найти кратчайший путь между вершинами ii и p[i][j]p[i][j], а также между p[i][j]p[i][j] и jj. Отсюда получается простой рекурсивный алгоритм восстановления кратчайшего пути.

+

3.8. Нахождение цикла отрицательного веса

+

Если в графе есть циклы отрицательного веса, то формально алгоритм Флойда-Уоршелла неприменим к такому графу.

+

На самом же деле, для тех пар вершин ii и jj, между которыми нельзя зайти в цикл отрицательного вес, алгоритм отработает корректно.

+

Для тех же пар вершин, ответа для которых не существует (по причине наличия отрицательного цикла на пути между ними), алгоритм Флойда найдёт в качестве ответа какое-то число (возможно, сильно отрицательное, но не обязательно). Тем не менее, можно улучшить алгоритм Флойда, чтобы он аккуратно обрабатывал такие пары вершин и выводил для них, например, - бесконечность.

+

Для этого можно сделать, например, следующий критерий "не существования пути". Итак, пусть на данном графе отработал обычный алгоритм Флойда. Тогда между вершинами ii и jj не существует кратчайшего пути тогда и только тогда, когда найдётся такая вершина tt, достижимая из ii и из которой достижима jj, для которой выполняется d[t][t]<0d[t][t] < 0.

+

Кроме того, при использовании алгоритма Флойда для графов с отрицательными циклами следует помнить, что возникающие в процессе работы расстояния могут сильно уходить в минус, экспоненциально с каждой фазой. Поэтому следует принять меры против целочисленного переполнения, ограничив все расстояния снизу какой-нибудь величиной (например, - Бесконечность).

+

3.9. Алгоритм Джонсона. Добавление фиктивного корня и фиктивных ребер для запуска алгоритма Форда-Беллмана

+

Алгоритм Джонсона

+
+

Алгоритм Джонсона позволяет найти кратчайшие пути между всеми парами вершин в течение времени O(V2log(V)+VE)O(V \cdot 2 \log(V) + VE). Для разреженных графов этот алгоритм ведет себя асимптотически быстрее алгоритма Флойда. Этот алгоритм либо возвращает матрицу кратчайших расстояний между всеми парами вершин, либо сообщение о том, что в графе существует цикл отрицательной длины.

+
+

В этом алгоритме используется метод изменения веса (англ. reweighting). Суть его заключается в том, что для заданного графа G строится новая весовая функция ωφ\omega_\varphi, неотрицательная для всех ребер графа G и сохраняющая кратчайшие пути. Такая весовая функция строится с помощью так называемой потенциальной функции.

+

Пусть φ:VR\varphi: V \rightarrow R — произвольное отображение из множества вершин в вещественные числа. Тогда новой весовой функцией будет ωφ(u,v)=φ(u,v)+φ(u)φ(v)\omega_\varphi(u,v)=\varphi(u,v)+\varphi(u)−\varphi(v).

+

Такая потенциальная функция строится добавлем фиктивной вершины ss в GG, из которой проведены ориентированные ребра нулевого веса во все остальные вершины графа, и запуском алгоритма Форда-Беллмана из нее (φ(v)\varphi(v) будет равно длине кратчайшего пути из ss в vv). На этом же этапе мы сможем обнаружить наличие отрицательного цикла в графе.

+

Теперь, когда мы знаем, что веса всех ребер неотрицательны, и кратчайшие пути сохранятся, можно запустить алгоритм Дейкстры из каждой вершины и таким образом найти кратчайшие расстояния между всеми парами вершин.

+

Добавление фиктивного корня и фиктивных ребер для запуска алгоритма Форда-Беллмана

+

Как в случае применения алгоритма Беллмана-Форда для обнаружения отрицательных циклов, существуют два способа сделать вес каждого ребра неотрицательным в произвольной сети без отрицательных циклов. Либо можно начать с истока в каждом сильно связном компоненте, либо добавить фиктивную вершину с ребром нулевой длины в каждую вершину сети. В любом случае получится остовный лес кратчайших путей, которым можно воспользоваться для присвоения весов вершинам (вес пути из корня в данную вершину в ее SPT).

+

4. Остовные деревья

+

4.1. Остовное дерево. Построение с помощью обхода в глубину и в ширину

+

Остовное дерево

+
+

Остовное дерево графа (англ. Spanning tree) — это дерево, подграф данного графа, с тем же числом вершин, что и у исходного графа. Неформально говоря, остовное дерево получается из исходного графа удалением максимального числа рёбер, входящих в циклы, но без нарушения связности графа. Остовное дерево включает в себя все n вершин исходного графа и содержит n1n-1 ребро.

+
+

Построение с помощью обхода в глубину и в ширину

+

Процедуры обхода графа в глубину и в ширину можно простым способом использовать для нахождения остовов. В обоих случаях достижение новой вершины графа uu из вершины vv вызывает "включение" в остовное дерево ребра (u,v)(u, v).

+
void Spisok::Depth_First_Search (Lref r)
+//Рекурсивный обход графа в глубину. r - указатель 
+//на структуру Вирта.
+{
+  Tref t;
+
+  t = (*r).Trail; cout<<(*r).Key; (*r).Flag = FALSE;
+  while (t!=NULL)
+  { if ((*(*t).Id).Flag) Depth_First_Search ((*t).Id); t = (*t).Next; }
+} 
+
+
void Spisok::Ostov_Depth (Lref r)
+//Рекурсивный обход графа в глубину, соединенный с
+//нахождением ребра остовного дерева.
+//r - указатель на структуру Вирта.
+{
+  Tref t;
+  Lref s;
+
+  s = r;
+  t = (*r).Trail; (*r).Flag = FALSE;
+  while ( t != NULL )
+  { if ((*(*t).Id).Flag) 
+    { cout << "(" << s->Key << "," << t->Id->Key << ") ";
+      Ostov_Depth ((*t).Id);
+    }
+    t = (*t).Next; 
+  }
+}
+
+

4.2. Определение минимального остовного дерева

+
+

Минимальное остовное дерево (англ. minimum spanning tree) графа — это его ациклический связный подграф, в который входят все его вершины, обладающий минимальным суммарным весом ребер. Заметим, что граф может содержать несколько минимальных остовных деревьев.

+
+

4.3. Теорема о разрезе. Доказательство

+

Теорема о разрезе

+

Теорема Форда — Фалкерсо́на — теорема о максимальном потоке в графе, тесно связанная с теоремой Менгера. +Звучит так: величина максимального потока в графе путей равна величине пропускной способности его минимального разреза.

+

Доказательство:

+

Достаточность: любой поток между вершинами t и s меньше или равен величине любого сечения. Пусть дан некоторый поток и некоторое сечение. Величина данного потока складывается из величин «грузов», перевозимых по всем возможным путям из вершины t в s. Каждый такой путь обязан иметь общее ребро с данным сечением. Так как по каждому ребру сечения суммарно нельзя перевести «груза» больше, чем его пропускная способность, поэтому сумма всех грузов меньше или равна сумме всех пропускных способностей рёбер данного сечения. Утверждение доказано.

+

Отсюда следует, что любой поток меньше или равен величине минимального сечения, а значит и максимальный поток меньше или равен величине минимального сечения.

+

4.4. Алгоритм Прима. Аналогия с алгоритмом Дейкстры. Оценка времени работы для различных реализаций очереди с приоритетом: бинарная куча, Фибоначчиева куча (последнее без доказательства)

+

Алгоритм Прима

+
+

Алгоритм Прима (англ. Prim's algorithm) — алгоритм поиска минимального остовного дерева (англ. minimum spanning tree, MST) во взвешенном неориентированном связном графе.

+
+

Аналогия с алгоритмом Дейкстры

+

Данный алгоритм очень похож на алгоритм Дейкстры. Будем последовательно строить поддерево FF ответа графе GG, поддерживая приоритетную очередь QQ из вершин GFG \setminus F, в которой ключом для вершины vv является min(ω(uv)(uV(F),(u,v)E(G)))\min(\omega(uv) (u \in V(F), (u, v) \in E(G))) — вес минимального ребра из вершин FF в вершины GFG \setminus F. Также для каждой вершины в очереди будем хранить p(v)p(v) — вершину uu, на которой достигается минимум в определении ключа. Дерево FF поддерживается неявно, и его ребра — это пары (v,p(v))(v,p(v)), где vGrQv \in G \setminus {r} \setminus Q, а rr — корень FF. Изначально FF пусто и значения ключей у всех вершин равны ++\infty. Выберём произвольную вершину rr и присвоим её ключу значение 0. На каждом шаге будем извлекать минимальную вершину vv из приоритетной очереди и релаксировать все ребра (v,u)(v, u), такие что uQu \in Q, выполняя при этом операцию decreaseKey\rm decreaseKey над очередью и обновление p(v)p(v). Ребро (v,p(v))(v,p(v)) при этом добавляется к ответу.

+

Оценка времени работы для различных реализаций очереди с приоритетом: бинарная куча, Фибоначчиева куча

+

Двоичная куча - O(ElogV)O(ElogV) +(Чтобы упростить операцию decreaseKey\rm decreaseKey можно написать кучу на основе сбалансированного бинарного дерева поиска. Тогда просто удалим вершину и добавим ее обратно уже с новым ключом. Асимптотика таких преобразований O(logn)O(logn). Если же делать с бинарной кучей, то вместо операции decreaseKey\rm decreaseKey, будем всегда просто добавлять вершину с новым ключом, если из кучи достали вершину с ключом, значение которого больше чем у нее уже стоит, просто игнорировать. Вершин в куче будет не больше 2n2 \cdot n, следовательно, операция extractMin\rm extractMin будет выполняться за O(log(n))O(log(\cdot n)), что равно O(logn)O(logn). Максимальное количество вершин, которое мы сможем достать, равняется количеству ребер, то есть m, поэтому общая асимптотика составит O(mlogn)O(mlogn), что хорошо только на разреженных графах.)

+

Фибоначчиева куча - O(VlogV+E)O(VlogV+E)

+

4.5. Алгоритм Прима. Доказательство корректности с помощью теоремы о разрезе

+

Приведем еще раз теорему о разрезе:

+
+

Для любого разбиения множества вершин V=ABV = A \cup B, существует минимальный остов, содержащий ee – минимальное по весу ребро, проходящее через разрез A,B\langle A, B \rangle

+
+

Доказательство. Возьмём минимальный остов без ee. Добавим ee, получится цикл. Два ребра этого цикла проходят через разрез A,B\langle A, B \rangle. Старое неменьше ee, удалим его. Если же в процессе построения min\rm min остова уже известно подмножество рёбер будущего min\rm min остова, эти рёбра задают компоненты связности. Мы можем сжать компоненты в вершины и для конденсации применить лемму о разрезе.

+

Следствие XX – подмножество рёбер некого минимального остова. Зафиксируем разрез V=ABV = A \cup B такой, что все рёбра XX не пересекают разрез. Тогда \exist минимальный остов, содержащий X{e}X \cup \{e\}, где ee – минимальное по весу ребро, проходящее через разрез A,B\langle A, B \rangle.

+

Доказательство корректности с помощью теоремы о разрезе

+

Разрез – текущее множество AA и дополнение VAV \setminus A.

+

4.6. Алгоритм Краскала. Доказательство корректности. Оценка времени работы

+

Алгоритм Краскала

+

Будем последовательно строить подграф FF графа GG ("растущий лес"), пытаясь на каждом шаге достроить FF до некоторого MSTMST. Начнем с того, что включим в FF все вершины графа GG. Теперь будем обходить множество E(G)E(G) в порядке неубывания весов ребер. Если очередное ребро ee соединяет вершины одной компоненты связности FF, то добавление его в остов приведет к возникновению цикла в этой компоненте связности. В таком случае, очевидно, ee не может быть включено в FF. Иначе ee соединяет разные компоненты связности FF, тогда существует S,T\langle S, T \rangle разрез такой, что одна из компонент связности составляет одну его часть, а оставшаяся часть графа — вторую. Тогда ee — минимальное ребро, пересекающее этот разрез. Значит, из леммы о безопасном ребре следует, что ee является безопасным, поэтому добавим это ребро в FF. На последнем шаге ребро соединит две оставшиеся компоненты связности, полученный подграф будет минимальным остовным деревом графа GG. Для проверки возможности добавления ребра используется система непересекающихся множеств.

+
Доказательство корректности
+

Обратимся к теореме о разрезе из Пункта 4.5 +Добавляем ребро (a,b)(a, b). Разрез – любой такой, что (a,b)(a, b) через него проходит.

+
Оценка времени работы
+

O(ElogE)O(ElogE).

+

4.7. Система непересекающихся множеств. Эвристика потенциалов без доказательства. Эвристика сжатия пути без доказательства. Почти константное время работы

+

4.8. Алгоритм Борувки. Доказательство(полное). Оценка времени работы

+

Алгоритм Борувки

+

Алгоритм состоит из нескольких шагов:

+
    +
  1. Изначально каждая вершина графа GG — тривиальное дерево, а ребра не принадлежат никакому дереву.
  2. +
  3. Для каждого дерева TT найдем минимальное инцидентное ему ребро. Добавим все такие ребра.
  4. +
  5. Повторяем шаг 2 пока в графе не останется только одно дерево TT.
  6. +
+

Данный алгоритм может работать неправильно, если в графе есть ребра равные по весу. Например, полный граф из трех вершин, вес каждого ребра равен один. В TT могут быть добавлены все три ребра. Избежать эту проблему можно, например, выбирая в первом пункте среди ребер, равных по весу, ребро с наименьшим номером.

+

Доказательство:

+

Обратимся к теореме о разрезе из Пункта 4.5 +Рёбра, которые хочет добавить Борувка, разобьём на компоненты связности. Каждая компонента – дерево, рёбра компоненты будем добавлять по одному от корня к листьям. Добавляя +очередное ребро ведущее в лист vv, используем разрез {v},V{v}\langle \{v\}, V \setminus \{v\} \rangle и лемму о разрезе.

+
Оценка времени работы
+

На ii-ой итерации внешнего цикла каждая компонента состоит как минимум из двух компонент из (i1)(i−1)-й итерации. Значит, на каждой итерации число компонент уменьшается как минимум в 2 раза. Тогда внешний цикл повторяется O(logV)O(logV) раз, так как количество компонент изначально равно количеству вершин. Что же касается внутреннего цикла, то он выполняется за O(E)O(E), где EE — количество рёбер в исходном графе. Следовательно конечное время работы алгоритма O(ElogV)O(ElogV).

+

4.9. Приближение решения задачи коммивояжера с помощью минимального остовного дерева

+
+

Алгоритм Кристофидеса или алгоритм Кристофидеса-Сердюкова — это алгоритм поиска приближённых решений задачи коммивояжёра для случаев, когда расстояния образуют метрическое пространство (симметричны и удовлетворяют неравенству треугольника).

+
+

Алгоритм является аппроксимационным алгоритмом, который гарантирует, что решения находятся в пределах 3/2 от длины оптимального решения. Алгоритм назван именем Никоса Кристофидеса и Анатолия Ивановича Сердюкова, которые независимо друг от друга нашли его в 1976, и он обладает лучшим аппроксимационным коэффициентом, который был доказан для задачи коммивояжёра на метрических пространствах общего вида, хотя известны лучшие приближения для некоторых специальных случаев.

+

Пусть G=(V,w)G=(V,w) будет представителем задачи коммивояжёра. То есть GG является полным графом на множестве вершин VV, а функция ww назначает неотрицательные вещественные веса каждому ребру графа GG. +Алгоритм можно описать на псевдокоде следующим образом:

+
    +
  1. Создаём минимальное остовное дерев TT графа GG.
  2. +
  3. Пусть OO будет набором вершин с нечётными степенями в TT. Согласно лемме о рукопожатиях, OO имеет чётное число вершин.
  4. +
  5. Находим совершенное паросочетание MM минимального веса в порождённом подграфе, заданным вершинами из OO.
  6. +
  7. Комбинируем рёбра MM и TT с образованием связного мультиграфа HH, в котором каждая вершина имеет чётную степень.
  8. +
  9. Образуем эйлеров цикл в HH.
  10. +
  11. Преобразуем цикл, найденный на предыдущем шаге, в гамильтонов цикл путём пропуска повторяющихся вершин (сокращение).
  12. +
+

5. Потоки в сетях

+

5.1. Определение сети. Определение потока. Физический смысл. Аналогия с законами Кирхгофа. Определение разреза. Понятия потока через разрез

+

Определение сети

+
+

Сеть (англ. flow network) представляет собой ориентированный граф, в котором каждое ребро имеет положительную пропускную способность (англ. capacity).

+
+

Определение потока

+
+

Потоком (англ. flow) ff в GG является действительная функция f:V×VRf: V \times V \rightarrow R, удоволетворяющая условиям:

+
+
    +
  1. f(u,v)=f(v,u)f(u,v) = −f(v,u) (антисимметричность)
  2. +
  3. f(u,v)c(u,v)f(u,v) \leq c(u,v) (ограничение пропускной способности), если ребра нет, то f(u,v)=0f(u,v)=0;
  4. +
  5. Σvf(u,v)=0\Sigma v \cdot f(u,v) = 0 для всех вершин uu, кроме ss и tt (закон сохранения потока).
  6. +
+
+

Величина потока ff определяется как f=ΣvVf(s,v)|f|=\Sigma v \in V \cdot f(s,v).

+
+

Физический смысл. Аналогия с законами Кирхгофа

+

Определение разреза

+
+

(s,t)(s,t) - разрезом (англ. s-t cut) S,T\langle S,T \rangle в сети GG называется пара множеств SS, TT, удоволетворяющих условиям: sSs \in S,tTt \in T и S=VTS = V \setminus T.

+
+

Понятия потока через разрез

+
+

Поток в разрезе (англ. flow in the cut) S,T\langle S,T \rangle обозначается f(S,T)f(S,T) и вычисляется по формуле: f(S,T)=ΣuSΣvTf(u,v)f(S,T)= \Sigma u \in S \cdot \Sigma v \in T \cdot f(u,v).

+
+

5.2. Доказательство факта, что поток через любой разрез одинаковый

+

f(S,T)=f(S,V)f(S,S)=f(S,V)=f(Ss,V)+f(s,V)=f(s,V)=ff(S,T) = f(S,V) − f(S,S) = f(S,V) = f(S \setminus s,V) + f(s,V) = f(s,V) = |f|

+

1-е равенство выполняется, так как суммы не пересекаются: f(S,V)=f(S,S)+f(S,T)f(S,V)=f(S,S)+f(S,T) +2-е равенство выполняется из-за антисимметричности: f(S,S)=f(S,S)=0f(S,S)=−f(S,S)=0 +3-е равенство выполняется, как и 1-е, из-за непересекающихся сумм +4-е равенство выполняется из-за сохранения потока

+

5.3. Понятие остаточной сети. Понятие дополняющего пути. Необходимость отсутствия дополняющего пути для максимальности потока

+

Понятие остаточной сети

+

Если задана некоторая транспортная сеть GG и поток ff, то остаточная сеть GfG_f - это сеть, состоящая из ребер с пропускными способностями, указывающими, как могут меняться потоки через ребра GG.

+

Понятие дополняющего пути

+

Для заданной транспортной сети и потока дополняющим путем (англ. augmenting path) является простой путь из истока в сток в остаточной сети.

+

Необходимость отсутствия дополняющего пути для максимальности потока

+

В случае существования дополняющего пути поток можно увеличить.

+

5.4. Теорема Форда-Фалкерсона

+
+

Теорема Форда — Фалкерсо́на — теорема о максимальном потоке в графе, тесно связанная с теоремой Менгера. Звучит так: величина максимального потока в графе путей равна величине пропускной способности его минимального разреза.

+
+

5.5. Алгоритм Форда-Фалкерсона. Поиск минимального разреза. Пример целочисленной сети, в котором алгоритм работает долго

+

Алгоритм Форда-Фалкерсона

+

Идея алгоритма заключается в следующем. Изначально величине потока присваивается значение 0: f(u,v)=0f(u,v)=0 для всех u,vVu, v \in V. Затем величина потока итеративно увеличивается посредством поиска увеличивающего пути (путь от источника ss к стоку tt, вдоль которого можно послать ненулевой поток). В данной статье рассматривается алгоритм, осуществляющий этот поиск с помощью обхода в глубину (dfs). Процесс повторяется, пока можно найти увеличивающий путь.

+

Неформальное описание:

+
    +
  1. Обнуляем все потоки. Остаточная сеть изначально совпадает с исходной сетью.
  2. +
  3. В остаточной сети находим любой путь из источника в сток. Если такого пути нет, останавливаемся.
  4. +
  5. Пускаем через найденный путь (он называется увеличивающим путём или увеличивающей цепью) максимально возможный поток: +
      +
    1. На найденном пути в остаточной сети ищем ребро с минимальной пропускной способностью cminc_{min}.
    2. +
    3. Для каждого ребра на найденном пути увеличиваем поток на cminc_{min}, а в противоположном ему — уменьшаем на cminc_{min}.
    4. +
    5. Модифицируем остаточную сеть. Для всех рёбер на найденном пути, а также для противоположных (антипараллельных) им рёбер, вычисляем новую пропускную способность. Если она стала ненулевой, добавляем ребро к остаточной сети, а если обнулилась, стираем его.
    6. +
    +
  6. +
  7. Возвращаемся на шаг 2.
  8. +
+

Поиск минимального разреза

+

Алгоритм Форда-Фалкерсона использует поиск в ширину для нахождения увеличивающего пути в сети и постепенно увеличивает поток через этот путь, пока не будет достигнут максимальный поток. Затем алгоритм ищет минимальный разрез, основываясь на значении потока.

+

5.6. Алгоритм Эдмондса-Карпа. Доказательство, что кратчайшее расстояние в остаточной сети не уменьшается

+

Алгоритм Эдмондса-Карпа

+
+

Алгоритм Эдмондса-Карпа является реализацией метода Форда-Фалкерсона, в которой в качестве дополняющего пути выбирается кратчайший по рёбрам путь в остаточной сети (длины всех рёбер равны 1).

+
+

Описание:

+
    +
  1. Положим все потоки равными нулю. Остаточная сеть изначально совпадает с исходной сетью.
  2. +
  3. В остаточной сети находим кратчайший путь из источника в сток. Если такого пути нет, останавливаемся.
  4. +
  5. Пускаем через найденный путь (он называется увеличивающим путём или увеличивающей цепью) максимально возможный поток: +
      +
    1. На найденном пути в остаточной сети ищем ребро с минимальной пропускной способностью cminc_{min}.
    2. +
    3. Для каждого ребра на найденном пути увеличиваем поток на cminc_{min}, а в противоположном ему — уменьшаем на cminc_{min}.
    4. +
    5. Модифицируем остаточную сеть. Для всех рёбер на найденном пути, а также для противоположных им рёбер, вычисляем новую пропускную способность. Если она стала ненулевой, добавляем ребро к остаточной сети, а если обнулилась, стираем его.
    6. +
    +
  6. +
  7. Возвращаемся на шаг 2.
  8. +
+

Доказательство, что кратчайшее расстояние в остаточной сети не уменьшаетcя:

+

Лемма:

+
+

Кратчайшее расстояние от истока до каждой вершины не уменьшается с выполнением каждой итерации, т.е. leveli+1[v]>=leveli[v]level_{i+1}[v] >= level_i[v], где нижний индекс обозначает номер фазы, перед которой взяты значения этих переменных.

+
+

Доказательство:

+

Зафиксируем произвольную фазу ii и произвольную вершину vv и рассмотрим любой кратчайший svs - v путь PP в сети Gi+1RG^R_{i+1} (напомним, так мы обозначаем остаточную сеть, взятую перед выполнением i+1i+1-ой фазы). Очевидно, длина пути PP равна leveli+1[v]level_{i+1}[v].

+

Заметим, что в остаточную сеть Gi+1RG^R_{i+1} могут входить только рёбра из GRG^R, а также рёбра, обратные рёбрам из GRG^R (это следует из определения остаточной сети). Рассмотрим два случая:

+

Путь PP содержит только рёбра из GRG^R. Тогда, понятно, длина пути PP больше либо равна leveli[v]level_i[v] (потому что leveli[v]level_i[v] по определению — длина кратчайшего пути), что и означает выполнение неравенства. Путь PP содержит как минимум одно ребро, не содержащееся в GRG^R (но обратное какому-то ребру из GRG^R). Рассмотрим первое такое ребро; пусть это будет ребро (u,w)(u,w). suws \rightarrow u \rightarrow w

+

Мы можем применить нашу лемму к вершине uu, потому что она подпадает под первый случай; итак, мы получаем неравенство leveli+1[u]>=leveli[u]level_{i+1}[u] >= level_i[u].

+

Теперь заметим, что поскольку ребро (u,w)(u, w) появилось в остаточной сети только после выполнения ii-ой фазы, то отсюда следует, что вдоль ребра (w,u)(w, u) был дополнительно пропущен какой-то поток; следовательно, ребро (w,u)(w, u) принадлежало слоистой сети перед ii-ой фазой, а потому leveli[u]=leveli[w]+1level_i[u] = level_i[w] + 1. Учтём, что по свойству кратчайших путей leveli+1[w]=leveli+1[u]+1level_{i+1}[w] = level_{i+1}[u] + 1, и объединяя это равенство с двумя предыдущими неравенствами, получаем: +leveli+1[w]>=leveli[w]+2level_{i+1}[w] >= level_i[w] + 2.

+

Теперь мы можем применять те же самые рассуждения ко всему оставшемуся пути до vv (т.е. что каждое инвертированное ребро добавляет к level\rm level как минимум два), и в итоге получим требуемое неравенство.

+

5.7. Общая оценка времени работы алгоритма Эдмондса-Карпа

+

Сложность алгоритма Эдмондса-Карпа равна O(VE2)O(VE^2).

+

5.8. Слоистая сеть. Алгоритм Диница. Оценка времени работы без доказательства

+

Слоистая сеть

+
+

Слоистая сеть — это сеть состоящая только из таких рёбер (v,u)(v, u) сети GG, что d(v)+1=d(u)d(v) + 1 = d(u), где dd - расстояние от истока.

+
+

Алгоритм Диница

+

Схема алгоритма:

+
    +
  1. Построить слоистую сеть по остаточной, если сток не достижим то завершить алгоритм
  2. +
  3. Пустить блокирующий поток в слоистой сети
  4. +
  5. Если алгоритм завершился, то в остаточной сети сток не достижим из истока, а значит не существует увеличивающего пути, следовательно найденный поток максимален.
  6. +
+

Оценка времени работы без доказательства

+

Так как расстояние строго увеличивается количество итераций алгоритма = O(V)O(V). В зависимости от выбора алгоритма поиска блокирующего потока время данного алгоритма может составить O(VE2),O(V2E)O(VE^2), O(V^2E) или O(V3)O(V^3).

+

6. RMQ. Sparse-table, дерево отрезков. LCA. Декартово дерево по неявному ключу

+

6.1. RSQ и RMQ. Sparse-table

+

RSQ и RMQ.

+

Дан числовой массив: A[0]A[0], A[1]A[1], ..., A[N1A[N−1.$

+

Нужно отвечать на запросы:

+
    +
  • для данных LL и RR вычислить сумму A[L]+...+A[R1]A[L] + ... + A[R−1];
  • +
  • (опционально) обновить элемент: A[i]:=xA[i] := x.
  • +
+

Операция — не обязательно сумма, а элементы — не обязательно числа.

+
    +
  • Сумма \rightarrow RSQ (range sum query)
  • +
  • Минимум \rightarrow RMQ (range minimum query)
  • +
+

6.2. Дерево отрезков. Обработка запросов от листьев. Обработка запросов от корня

+

Дерево отрезков

+

Дерево отрезков — это структура данных, которая позволяет эффективно (т.е. за асимптотику O(logn)O(log n)) реализовать операции следующего вида: нахождение суммы/минимума элементов массива в заданном отрезке (a[l...r]a[l ... r], где ll и rr поступают на вход алгоритма), при этом дополнительно возможно изменение элементов массива: как изменение значения одного элемента, так и изменение элементов на целом подотрезке массива (т.е. разрешается присвоить всем элементам a[l...r]a[l ... r] какое-либо значение, либо прибавить ко всем элементам массива какое-либо число).

+

Вообще, дерево отрезков — очень гибкая структура, и число задач, решаемых ей, теоретически неограниченно. Помимо приведённых выше видов операций с деревьями отрезков, также возможны и гораздо более сложные операции (см. раздел "Усложнённые версии дерева отрезков"). В частности, дерево отрезков легко обобщается на большие размерности: например, для решения задачи о поиске суммы/минимума в некотором подпрямоугольнике данной матрицы (правда, уже только за время O(log2n)O(log^2 n)).

+

Важной особенностью деревьев отрезков является то, что они потребляют линейный объём памяти: стандартному дереву отрезков требуется порядка 4n4n элементов памяти для работы над массивом размера nn.

+

Обработка запросов от листьев. Обработка запросов от корня

+

В дереве отрезков обработка запросов может осуществляться как от листьев (bottom-up), так и от корня (top-down). Это связано с тем, как информация распространяется вдоль дерева при выполнении запросов.

+
    +
  1. +

    Обработка запросов от листьев (Bottom-Up):

    +
      +
    • Идея заключается в том, что сначала вычисляются значения для листьев (первоначальные элементы массива), а затем эта информация постепенно передается вверх по дереву для расчета значений на более высоких уровнях.
    • +
    • Когда поступает запрос на определенный отрезок, начинаются вычисления от соответствующих листьев. Затем результаты объединяются на более высоких уровнях дерева, пока не достигнется корень.
    • +
    • Этот подход обеспечивает эффективность расчетов для запросов на отрезках.
    • +
    +
  2. +
  3. +

    Обработка запросов от корня (Top-Down):

    +
      +
    • Здесь запрос начинается с корня дерева, и информация передается вниз по дереву до тех пор, пока не достигнут нужные листья (или пока не выполнены условия запроса).
    • +
    • Этот метод может быть полезен в случаях, когда запросы связаны с каким-то условием, которое позволяет пропускать некоторые поддеревья, не вычисляя их полностью.
    • +
    • Позволяет оптимизировать запросы, если известно, что результаты на некоторых уровнях дерева не повлияют на результат запроса.
    • +
    +
  4. +
+

Реализация обработки запросов от листьев или от корня зависит от конкретных требований задачи и структуры дерева отрезков. В некоторых случаях может быть полезно использовать комбинацию обоих методов для оптимизации производительности и удовлетворения специфических требований запросов.

+

6.3. Изменение значения в массиве, обновление дерева отрезков. Множественные операции

+

Изменение значения в массиве, обновление дерева отрезков.

+

Напомним, что запрос обновления получает на вход индекс ii и значение xx, и перестраивает дерево отрезков таким образом, чтобы оно соответствовало новому значению A[i]:=xA[i] := x. Этот запрос должен также выполняться за время O(logn)O(log n).

+

Это более простой запрос по сравнению с запросом подсчёта суммы. Дело в том, что элемент A[i]A[i] участвует только в относительно небольшом числе вершин дерева отрезков: а именно, в O(logn)O(log n) вершинах — по одной с каждого уровня.

+

Тогда понятно, что запрос обновления можно реализовать как рекурсивную функцию: ей передаётся текущая вершина дерева отрезков, и эта функция выполняет рекурсивный вызов от одного из двух своих сыновей (от того, который содержит позицию i в своём отрезке), а после этого — пересчитывает значение суммы в текущей вершине точно таким же образом, как мы это делали при построении дерева отрезков (т.е. как сумма значений по обоим сыновьям текущей вершины).

+
void update (int v, int tl, int tr, int pos, int new_val) {
+  if (tl == tr)
+    t[v] = new_val;
+  else {
+    int tm = (tl + tr) / 2;
+    if (pos <= tm)
+      update (v*2, tl, tm, pos, new_val);
+    else
+      update (v*2+1, tm+1, tr, pos, new_val);
+    t[v] = t[v*2] + t[v*2+1];
+  }
+}
+
+

Множественные операции

+

Множественные операции в дереве отрезков относятся к расширенной концепции структуры данных "дерево отрезков", которая позволяет эффективно выполнять несколько операций на отрезках массива или последовательности данных. Обычно дерево отрезков предназначено для выполнения операций типа запроса на сумму, минимум, максимум на определенном подотрезке массива. Однако, при необходимости, можно расширить возможности дерева отрезков для поддержки нескольких операций.

+

Примеры множественных операций в дереве отрезков могут включать в себя:

+
    +
  1. +

    Сумма и обновление (Range Sum Query and Range Update): Помимо запросов на сумму элементов на определенном отрезке, можно добавить операции обновления, изменяющие значения элементов на определенном отрезке.

    +
  2. +
  3. +

    Минимум/максимум и обновление (Range Minimum/Maximum Query and Range Update): Поддержка запросов на поиск минимума или максимума на определенном отрезке, а также операций обновления значений элементов.

    +
  4. +
  5. +

    Подсчет количества элементов в диапазоне с заданным условием (Count Elements in Range with a Given Condition): Дерево отрезков может быть настроено для подсчета количества элементов, удовлетворяющих определенному условию, на определенном отрезке.

    +
  6. +
  7. +

    Операции нахождения k-го порядкового статистического элемента (K-th Order Statistics Operations): Поиск kk-го минимального/максимального элемента на определенном отрезке.

    +
  8. +
  9. +

    Сложные агрегатные функции: Различные агрегатные функции, такие как нахождение среднего значения, медианы и др., могут быть реализованы с использованием множественных операций.

    +
  10. +
+

Реализация множественных операций требует изменений в структуре дерева отрезков и логике обработки запросов и обновлений. Это может потребовать более сложного кода, но в то же время предоставляет более широкий спектр возможностей для решения различных задач.

+

6.4. LCA. Метод двоичного подъёма

+

LCA

+

Наименьший общий предок (нижайший общий предок) вершин uu и vv в корневом дереве TT — наиболее удалённая от корня дерева вершина, лежащая на обоих путях от uu и vv до корня, т. е. являющаяся предком обеих вершин. Общепринятое сокращение — LCALCA от англ. lowest (least) common ancestor.

+

Метод двоичного подъёма

+
+

Метод двоичного подъёма — один из самых простых методов для решения задачи LCA в online. Он не использует метод решения задачи RMQ и основан на методе динамического программирования.

+
+
function preprocess():
+     int[] p = dfs(0)
+     for i = 1 to n
+        dp[i][0] = p[i]
+     for j = 1 to log(n)
+        for i = 1 to n
+           dp[i][j] = dp[dp[i][j - 1]][j - 1]
+  
+  int lca(int v, int u):
+     if d[v] > d[u]
+        swap(v, u)
+     for i = log(n) downto 0
+        if d[dp[u][i]] - d[v] >= 0
+           u = dp[u][i]
+     if v == u
+        return v
+     for i = log(n) downto 0
+        if dp[v][i] != dp[u][i]
+           v = dp[v][i]
+           u = dp[u][i]
+     return p[v]
+
+

6.5. Декартово дерево по неявному ключу. Интерфейс быстрого массива: Доступ к элементу в позиции i, Вставка элемента в позицию i, Удаление элемента из позиции i, Конкатенация двух массивов, разделение массива на два

+

Декартово дерево по неявному ключу

+

Декартово дерево по неявному ключу - это структура данных, объединяющая свойства декартова дерева и быстрого массива (cartesian tree on implicit key). Декартово дерево основано на двух параметрах: приоритете (обычно случайном) и ключе элемента. В случае декартова дерева по неявному ключу, элементы упорядочены по порядку, заданному индексами массива.

+

Интерфейс быстрого массива

+
    +
  1. +

    Доступ к элементу в позиции i:

    +
      +
    • Так как элементы упорядочены по неявному ключу (порядковому номеру), доступ к элементу в позиции i выполняется, обращаясь к элементу с ключом i.
    • +
    +
  2. +
  3. +

    Вставка элемента в позицию i:

    +
      +
    • Вставка элемента в позицию i может быть выполнена путем создания нового узла с соответствующим приоритетом и неявным ключом i, затем соединения этого нового узла с существующими поддеревьями вокруг позиции i.
    • +
    +
  4. +
  5. +

    Удаление элемента из позиции i:

    +
      +
    • Удаление элемента из позиции i подразумевает разделение декартова дерева на два поддерева: одно до позиции i и второе после позиции i. Затем оба поддерева могут быть объединены, игнорируя узел с ключом i.
    • +
    +
  6. +
+

Декартово дерево по неявному ключу обеспечивает эффективный доступ к элементам по индексу, а также эффективную вставку и удаление элементов, поддерживая при этом свойства декартова дерева.

+

Конкатенация двух массивов

+

Декартово дерево можно использовать для эффективной конкатенации двух массивов и разделения массива на две части, представляя массив в виде декартова дерева по неявному ключу.

+

Для конкатенации двух массивов, представленных в виде декартова дерева, можно выполнить следующие шаги:

+
    +
  1. Создать новый узел, который станет корнем нового дерева.
  2. +
  3. Как корень нового дерева выбрать узел с максимальным приоритетом из обоих деревьев.
  4. +
  5. Сделать корень дерева с максимальным приоритетом корнем нового дерева.
  6. +
  7. Поддерево с меньшим приоритетом становится левым (или правым) поддеревом корня нового дерева.
  8. +
  9. Рекурсивно повторить процесс для поддеревьев, соответствующих двум исходным деревьям.
  10. +
+

Разделение массива на два

+

Для разделения массива на две части в декартовом дереве, можно воспользоваться тем фактом, что приоритеты узлов определяют порядок элементов. Мы ищем узел, чей неявный ключ соответствует индексу разделения.

+
    +
  1. Выполняем поиск в декартовом дереве по индексу разделения.
  2. +
  3. Получаем поддерево с корнем, у которого неявный ключ равен индексу разделения.
  4. +
  5. Разделяем это поддерево на два: левое поддерево становится левой частью, а правое поддерево становится правой частью.
  6. +
+ + + + + \ No newline at end of file diff --git a/doc/Theory.md b/doc/Theory.md index cd5202a..657d1c4 100644 --- a/doc/Theory.md +++ b/doc/Theory.md @@ -1,139 +1,140 @@ -# Теория по алгоритмам +# Теория по алгоритмам ## Содержание -- [Теория по алгоритмам ](#теория-по-алгоритмам-) - - [Содержание](#содержание) - - [1. Обходы графа](#1-обходы-графа) - - [1.1. Ориентированный граф, псевдограф. Неориентированный граф, псевдограф. Связность в неориентированном графе, компоненты связности. Слабая и сильная связность в ориентированном графе. Компоненты слабой, сильной связности](#11-ориентированный-граф-псевдограф-неориентированный-граф-псевдограф-связность-в-неориентированном-графе-компоненты-связности-слабая-и-сильная-связность-в-ориентированном-графе-компоненты-слабой-сильной-связности) - - [Ориентированный граф](#ориентированный-граф) - - [Неориентированный граф](#неориентированный-граф) - - [1.2. Обход в глубину. Цвета вершин. Времена входа и выхода. Лемма о белых путях (с доказательством)](#12-обход-в-глубину-цвета-вершин-времена-входа-и-выхода-лемма-о-белых-путях-с-доказательством) - - [Обход в глубину](#обход-в-глубину) - - [Лемма о белых путях](#лемма-о-белых-путях) - - [1.3. Проверка связности неориентированного графа. Поиск цикла в неориентированном и ориентированном графе. Топологическая сортировка](#13-проверка-связности-неориентированного-графа-поиск-цикла-в-неориентированном-и-ориентированном-графе-топологическая-сортировка) - - [Проверка связности неориентированного графа](#проверка-связности-неориентированного-графа) - - [Поиск цикла в неориентированном и ориентированном графе](#поиск-цикла-в-неориентированном-и-ориентированном-графе) - - [Топологическая сортировка](#топологическая-сортировка) - - [1.4. Нахождение компонент сильной связности. Алгоритм Косарайю с доказательством корректности(концепция). Алгоритм Тарьяна без доказательства корректности](#14-нахождение-компонент-сильной-связности-алгоритм-косарайю-с-доказательством-корректностиконцепция-алгоритм-тарьяна-без-доказательства-корректности) - - [Нахождение компонент сильной связности](#нахождение-компонент-сильной-связности) - - [Алгоритм Косарайю с доказательством корректности(концепция)](#алгоритм-косарайю-с-доказательством-корректностиконцепция) - - [Алгоритм Тарьяна без доказательства корректности](#алгоритм-тарьяна-без-доказательства-корректности) - - [1.5. Компоненты реберной двусвязности. Мосты. Поиск мостов](#15-компоненты-реберной-двусвязности-мосты-поиск-мостов) - - [Компоненты реберной двусвязности](#компоненты-реберной-двусвязности) - - [Мосты](#мосты) - - [Поиск мостов](#поиск-мостов) - - [1.6. Компоненты вершинной двусвязности. Точки сочленения. Поиск точек сочленения](#16-компоненты-вершинной-двусвязности-точки-сочленения-поиск-точек-сочленения) - - [Компоненты вершинной двусвязности](#компоненты-вершинной-двусвязности) - - [Точки сочленения](#точки-сочленения) - - [Поиск точек сочленения](#поиск-точек-сочленения) - - [1.7. Волновой алгоритм. Обход в ширину (применение очереди в волновом алгоритме)](#17-волновой-алгоритм-обход-в-ширину-применение-очереди-в-волновом-алгоритме) - - [Волновой алгоритм](#волновой-алгоритм) - - [Обход в ширину (применение очереди в волновом алгоритме)](#обход-в-ширину-применение-очереди-в-волновом-алгоритме) - - [1.8. Критерий существования Эйлерова пути и цикла в ориентированном и неориентированном графе. Поиск эйлерова пути и цикла](#18-критерий-существования-эйлерова-пути-и-цикла-в-ориентированном-и-неориентированном-графе-поиск-эйлерова-пути-и-цикла) - - [Критерий существования Эйлерова пути и цикла в неориентированном графе](#критерий-существования-эйлерова-пути-и-цикла-в-неориентированном-графе) - - [Критерий существования Эйлерова пути и цикла в ориентированном графе](#критерий-существования-эйлерова-пути-и-цикла-в-ориентированном-графе) - - [Поиск эйлерова пути и цикла](#поиск-эйлерова-пути-и-цикла) - - [2. Планарность графа](#2-планарность-графа) - - [2.1. Формула Эйлера. Теорема Портнягина-Куратовского. Теорема Вагнера](#21-формула-эйлера-теорема-портнягина-куратовского-теорема-вагнера) - - [Формула Эйлера](#формула-эйлера) - - [Теорема Портнягина-Куратовского](#теорема-портнягина-куратовского) - - [Теорема Вагнера](#теорема-вагнера) - - [2.2. Гамма алгоритм. Контактная вершина](#22-гамма-алгоритм-контактная-вершина) - - [Гамма алгоритм](#гамма-алгоритм) - - [2.3. Теорема о корректности Гамма алгоритма. Асимптотика алгоритма](#23-теорема-о-корректности-гамма-алгоритма-асимптотика-алгоритма) - - [Теорема о корректности Гамма алгоритма](#теорема-о-корректности-гамма-алгоритма) - - [3. Кратчайшие пути во взвешенном графе](#3-кратчайшие-пути-во-взвешенном-графе) - - [3.1. Алгоритм Дейкстры. Доказательство корректности(с доказательством). Оценка времени работы. Дерево кратчайших путей](#31-алгоритм-дейкстры-доказательство-корректностис-доказательством-оценка-времени-работы-дерево-кратчайших-путей) - - [Алгоритм Дейкстры](#алгоритм-дейкстры) - - [Доказательство корректности](#доказательство-корректности) - - [Оценка времени работы](#оценка-времени-работы) - - [Дерево кратчайших путей](#дерево-кратчайших-путей) - - [3.2. Потенциалы. Условие применимости алгоритма Дейкстры для измененных длин ребер. Потенциал $\\pi(v) = \\rho(v, t)$](#32-потенциалы-условие-применимости-алгоритма-дейкстры-для-измененных-длин-ребер-потенциал-piv--rhov-t) - - [Потенциалы](#потенциалы) - - [Условие применимости алгоритма Дейкстры для измененных длин ребер](#условие-применимости-алгоритма-дейкстры-для-измененных-длин-ребер) - - [Потенциал $\\pi(v) = \\rho(v, t)$](#потенциал-piv--rhov-t) - - [3.3. Алгоритм A\*. Условие монотонности на эвристику. Примеры эвристик](#33-алгоритм-a-условие-монотонности-на-эвристику-примеры-эвристик) - - [Алгоритм A\*](#алгоритм-a) - - [Примеры эвристик](#примеры-эвристик) - - [3.4. Алгоритм Форда-Беллмана. Хранение в матрице: $D\_{vk}$ равно длине кратчайшего пути до вершины $v$ за ровно $k$ ребер (не более $k$ ребер). Доказательство корректности (полное). Оценка времени работы](#34-алгоритм-форда-беллмана-хранение-в-матрице-d_vk-равно-длине-кратчайшего-пути-до-вершины-v-за-ровно-k-ребер-не-более-k-ребер-доказательство-корректности-полное-оценка-времени-работы) - - [Доказательство корректности(полное)](#доказательство-корректностиполное) - - [Оценка времени работы](#оценка-времени-работы-1) - - [3.5. Восстановление пути. Детектирование цикла отрицательного веса. Поиск самого цикла](#35-восстановление-пути-детектирование-цикла-отрицательного-веса-поиск-самого-цикла) - - [Восстановление пути](#восстановление-пути) - - [Детектирование цикла отрицательного веса](#детектирование-цикла-отрицательного-веса) - - [Поиск самого цикла](#поиск-самого-цикла) - - [3.6. Нахождение кратчайших путей с учетом циклов отрицательного веса](#36-нахождение-кратчайших-путей-с-учетом-циклов-отрицательного-веса) - - [3.7. Алгоритм Флойда. Доказательство (концепция). Восстановление пути](#37-алгоритм-флойда-доказательство-концепция-восстановление-пути) - - [Алгоритм Флойда](#алгоритм-флойда) - - [Доказательство (концепция)](#доказательство-концепция) - - [Восстановление пути](#восстановление-пути-1) - - [3.8. Нахождение цикла отрицательного веса](#38-нахождение-цикла-отрицательного-веса) - - [3.9. Алгоритм Джонсона. Добавление фиктивного корня и фиктивных ребер для запуска алгоритма Форда-Беллмана](#39-алгоритм-джонсона-добавление-фиктивного-корня-и-фиктивных-ребер-для-запуска-алгоритма-форда-беллмана) - - [Алгоритм Джонсона](#алгоритм-джонсона) - - [Добавление фиктивного корня и фиктивных ребер для запуска алгоритма Форда-Беллмана](#добавление-фиктивного-корня-и-фиктивных-ребер-для-запуска-алгоритма-форда-беллмана) - - [4. Остовные деревья](#4-остовные-деревья) - - [4.1. Остовное дерево. Построение с помощью обхода в глубину и в ширину](#41-остовное-дерево-построение-с-помощью-обхода-в-глубину-и-в-ширину) - - [Остовное дерево](#остовное-дерево) - - [Построение с помощью обхода в глубину и в ширину](#построение-с-помощью-обхода-в-глубину-и-в-ширину) - - [4.2. Определение минимального остовного дерева](#42-определение-минимального-остовного-дерева) - - [4.3. Теорема о разрезе. Доказательство](#43-теорема-о-разрезе-доказательство) - - [Теорема о разрезе](#теорема-о-разрезе) - - [4.4. Алгоритм Прима. Аналогия с алгоритмом Дейкстры. Оценка времени работы для различных реализаций очереди с приоритетом: бинарная куча, Фибоначчиева куча (последнее без доказательства)](#44-алгоритм-прима-аналогия-с-алгоритмом-дейкстры-оценка-времени-работы-для-различных-реализаций-очереди-с-приоритетом-бинарная-куча-фибоначчиева-куча-последнее-без-доказательства) - - [Алгоритм Прима](#алгоритм-прима) - - [Аналогия с алгоритмом Дейкстры](#аналогия-с-алгоритмом-дейкстры) - - [Оценка времени работы для различных реализаций очереди с приоритетом: бинарная куча, Фибоначчиева куча](#оценка-времени-работы-для-различных-реализаций-очереди-с-приоритетом-бинарная-куча-фибоначчиева-куча) - - [4.5. Алгоритм Прима. Доказательство корректности с помощью теоремы о разрезе](#45-алгоритм-прима-доказательство-корректности-с-помощью-теоремы-о-разрезе) - - [Доказательство корректности с помощью теоремы о разрезе](#доказательство-корректности-с-помощью-теоремы-о-разрезе) - - [4.6. Алгоритм Краскала. Доказательство корректности. Оценка времени работы](#46-алгоритм-краскала-доказательство-корректности-оценка-времени-работы) - - [Алгоритм Краскала](#алгоритм-краскала) - - [Доказательство корректности](#доказательство-корректности-1) - - [Оценка времени работы](#оценка-времени-работы-2) - - [4.7. Система непересекающихся множеств. Эвристика потенциалов без доказательства. Эвристика сжатия пути без доказательства. Почти константное время работы](#47-система-непересекающихся-множеств-эвристика-потенциалов-без-доказательства-эвристика-сжатия-пути-без-доказательства-почти-константное-время-работы) - - [4.8. Алгоритм Борувки. Доказательство(полное). Оценка времени работы](#48-алгоритм-борувки-доказательствополное-оценка-времени-работы) - - [Алгоритм Борувки](#алгоритм-борувки) - - [Оценка времени работы](#оценка-времени-работы-3) - - [4.9. Приближение решения задачи коммивояжера с помощью минимального остовного дерева](#49-приближение-решения-задачи-коммивояжера-с-помощью-минимального-остовного-дерева) - - [5. Потоки в сетях](#5-потоки-в-сетях) - - [5.1. Определение сети. Определение потока. Физический смысл. Аналогия с законами Кирхгофа. Определение разреза. Понятия потока через разрез](#51-определение-сети-определение-потока-физический-смысл-аналогия-с-законами-кирхгофа-определение-разреза-понятия-потока-через-разрез) - - [Определение сети](#определение-сети) - - [Определение потока](#определение-потока) - - [Физический смысл. Аналогия с законами Кирхгофа](#физический-смысл-аналогия-с-законами-кирхгофа) - - [Определение разреза](#определение-разреза) - - [Понятия потока через разрез](#понятия-потока-через-разрез) - - [5.2. Доказательство факта, что поток через любой разрез одинаковый](#52-доказательство-факта-что-поток-через-любой-разрез-одинаковый) - - [5.3. Понятие остаточной сети. Понятие дополняющего пути. Необходимость отсутствия дополняющего пути для максимальности потока](#53-понятие-остаточной-сети-понятие-дополняющего-пути-необходимость-отсутствия-дополняющего-пути-для-максимальности-потока) - - [Понятие остаточной сети](#понятие-остаточной-сети) - - [Понятие дополняющего пути](#понятие-дополняющего-пути) - - [Необходимость отсутствия дополняющего пути для максимальности потока](#необходимость-отсутствия-дополняющего-пути-для-максимальности-потока) - - [5.4. Теорема Форда-Фалкерсона](#54-теорема-форда-фалкерсона) - - [5.5. Алгоритм Форда-Фалкерсона. Поиск минимального разреза. Пример целочисленной сети, в котором алгоритм работает долго](#55-алгоритм-форда-фалкерсона-поиск-минимального-разреза-пример-целочисленной-сети-в-котором-алгоритм-работает-долго) - - [Алгоритм Форда-Фалкерсона](#алгоритм-форда-фалкерсона) - - [Поиск минимального разреза](#поиск-минимального-разреза) - - [5.6. Алгоритм Эдмондса-Карпа. Доказательство, что кратчайшее расстояние в остаточной сети не уменьшается](#56-алгоритм-эдмондса-карпа-доказательство-что-кратчайшее-расстояние-в-остаточной-сети-не-уменьшается) - - [Алгоритм Эдмондса-Карпа](#алгоритм-эдмондса-карпа) - - [5.7. Общая оценка времени работы алгоритма Эдмондса-Карпа](#57-общая-оценка-времени-работы-алгоритма-эдмондса-карпа) - - [5.8. Слоистая сеть. Алгоритм Диница. Оценка времени работы без доказательства](#58-слоистая-сеть-алгоритм-диница-оценка-времени-работы-без-доказательства) - - [Слоистая сеть](#слоистая-сеть) - - [Алгоритм Диница](#алгоритм-диница) - - [Оценка времени работы без доказательства](#оценка-времени-работы-без-доказательства) - - [6. RMQ. Sparse-table, дерево отрезков. LCA. Декартово дерево по неявному ключу](#6-rmq-sparse-table-дерево-отрезков-lca-декартово-дерево-по-неявному-ключу) - - [6.1. RSQ и RMQ. Sparse-table](#61-rsq-и-rmq-sparse-table) - - [RSQ и RMQ.](#rsq-и-rmq) - - [6.2. Дерево отрезков. Обработка запросов от листьев. Обработка запросов от корня](#62-дерево-отрезков-обработка-запросов-от-листьев-обработка-запросов-от-корня) - - [Дерево отрезков](#дерево-отрезков) - - [Обработка запросов от листьев. Обработка запросов от корня](#обработка-запросов-от-листьев-обработка-запросов-от-корня) - - [6.3. Изменение значения в массиве, обновление дерева отрезков. Множественные операции](#63-изменение-значения-в-массиве-обновление-дерева-отрезков-множественные-операции) - - [Изменение значения в массиве, обновление дерева отрезков.](#изменение-значения-в-массиве-обновление-дерева-отрезков) - - [Множественные операции](#множественные-операции) - - [6.4. LCA. Метод двоичного подъёма](#64-lca-метод-двоичного-подъёма) - - [LCA](#lca) - - [Метод двоичного подъёма](#метод-двоичного-подъёма) - - [6.5. Декартово дерево по неявному ключу. Интерфейс быстрого массива: Доступ к элементу в позиции i, Вставка элемента в позицию i, Удаление элемента из позиции i, Конкатенация двух массивов, разделение массива на два](#65-декартово-дерево-по-неявному-ключу-интерфейс-быстрого-массива-доступ-к-элементу-в-позиции-i-вставка-элемента-в-позицию-i-удаление-элемента-из-позиции-i-конкатенация-двух-массивов-разделение-массива-на-два) - - [Декартово дерево по неявному ключу](#декартово-дерево-по-неявному-ключу) - - [Интерфейс быстрого массива](#интерфейс-быстрого-массива) - - [Конкатенация двух массивов](#конкатенация-двух-массивов) - - [Разделение массива на два](#разделение-массива-на-два) +- [Содержание](#содержание) +- [1. Обходы графа](#1-обходы-графа) + - [1.1. Ориентированный граф, псевдограф. Неориентированный граф, псевдограф. Связность в неориентированном графе, компоненты связности. Слабая и сильная связность в ориентированном графе. Компоненты слабой, сильной связности](#11-ориентированный-граф-псевдограф-неориентированный-граф-псевдограф-связность-в-неориентированном-графе-компоненты-связности-слабая-и-сильная-связность-в-ориентированном-графе-компоненты-слабой-сильной-связности) + - [Ориентированный граф](#ориентированный-граф) + - [Неориентированный граф](#неориентированный-граф) + - [1.2. Обход в глубину. Цвета вершин. Времена входа и выхода. Лемма о белых путях (с доказательством)](#12-обход-в-глубину-цвета-вершин-времена-входа-и-выхода-лемма-о-белых-путях-с-доказательством) + - [Обход в глубину](#обход-в-глубину) + - [Лемма о белых путях](#лемма-о-белых-путях) + - [1.3. Проверка связности неориентированного графа. Поиск цикла в неориентированном и ориентированном графе. Топологическая сортировка](#13-проверка-связности-неориентированного-графа-поиск-цикла-в-неориентированном-и-ориентированном-графе-топологическая-сортировка) + - [Проверка связности неориентированного графа](#проверка-связности-неориентированного-графа) + - [Поиск цикла в неориентированном и ориентированном графе](#поиск-цикла-в-неориентированном-и-ориентированном-графе) + - [Топологическая сортировка](#топологическая-сортировка) + - [1.4. Нахождение компонент сильной связности. Алгоритм Косарайю с доказательством корректности(концепция). Алгоритм Тарьяна без доказательства корректности](#14-нахождение-компонент-сильной-связности-алгоритм-косарайю-с-доказательством-корректностиконцепция-алгоритм-тарьяна-без-доказательства-корректности) + - [Нахождение компонент сильной связности](#нахождение-компонент-сильной-связности) + - [Алгоритм Косарайю с доказательством корректности(концепция)](#алгоритм-косарайю-с-доказательством-корректностиконцепция) + - [Алгоритм Тарьяна без доказательства корректности](#алгоритм-тарьяна-без-доказательства-корректности) + - [1.5. Компоненты реберной двусвязности. Мосты. Поиск мостов](#15-компоненты-реберной-двусвязности-мосты-поиск-мостов) + - [Компоненты реберной двусвязности](#компоненты-реберной-двусвязности) + - [Мосты](#мосты) + - [Поиск мостов](#поиск-мостов) + - [1.6. Компоненты вершинной двусвязности. Точки сочленения. Поиск точек сочленения](#16-компоненты-вершинной-двусвязности-точки-сочленения-поиск-точек-сочленения) + - [Компоненты вершинной двусвязности](#компоненты-вершинной-двусвязности) + - [Точки сочленения](#точки-сочленения) + - [Поиск точек сочленения](#поиск-точек-сочленения) + - [1.7. Волновой алгоритм. Обход в ширину (применение очереди в волновом алгоритме)](#17-волновой-алгоритм-обход-в-ширину-применение-очереди-в-волновом-алгоритме) + - [Волновой алгоритм](#волновой-алгоритм) + - [Обход в ширину (применение очереди в волновом алгоритме)](#обход-в-ширину-применение-очереди-в-волновом-алгоритме) + - [1.8. Критерий существования Эйлерова пути и цикла в ориентированном и неориентированном графе. Поиск эйлерова пути и цикла](#18-критерий-существования-эйлерова-пути-и-цикла-в-ориентированном-и-неориентированном-графе-поиск-эйлерова-пути-и-цикла) + - [Критерий существования Эйлерова пути и цикла в неориентированном графе](#критерий-существования-эйлерова-пути-и-цикла-в-неориентированном-графе) + - [Критерий существования Эйлерова пути и цикла в ориентированном графе](#критерий-существования-эйлерова-пути-и-цикла-в-ориентированном-графе) + - [Поиск эйлерова пути и цикла](#поиск-эйлерова-пути-и-цикла) +- [2. Планарность графа](#2-планарность-графа) + - [2.1. Формула Эйлера. Теорема Портнягина-Куратовского. Теорема Вагнера](#21-формула-эйлера-теорема-портнягина-куратовского-теорема-вагнера) + - [Формула Эйлера](#формула-эйлера) + - [Теорема Портнягина-Куратовского](#теорема-портнягина-куратовского) + - [Теорема Вагнера](#теорема-вагнера) + - [2.2. Гамма алгоритм. Контактная вершина](#22-гамма-алгоритм-контактная-вершина) + - [Гамма алгоритм](#гамма-алгоритм) + - [2.3. Теорема о корректности Гамма алгоритма. Асимптотика алгоритма](#23-теорема-о-корректности-гамма-алгоритма-асимптотика-алгоритма) + - [Теорема о корректности Гамма алгоритма](#теорема-о-корректности-гамма-алгоритма) +- [3. Кратчайшие пути во взвешенном графе](#3-кратчайшие-пути-во-взвешенном-графе) + - [3.1. Алгоритм Дейкстры. Доказательство корректности(с доказательством). Оценка времени работы. Дерево кратчайших путей](#31-алгоритм-дейкстры-доказательство-корректностис-доказательством-оценка-времени-работы-дерево-кратчайших-путей) + - [Алгоритм Дейкстры](#алгоритм-дейкстры) + - [Доказательство корректности](#доказательство-корректности) + - [Оценка времени работы](#оценка-времени-работы) + - [Дерево кратчайших путей](#дерево-кратчайших-путей) + - [3.2. Потенциалы. Условие применимости алгоритма Дейкстры для измененных длин ребер. Потенциал $\\pi(v) = \\rho(v, t)$](#32-потенциалы-условие-применимости-алгоритма-дейкстры-для-измененных-длин-ребер-потенциал-piv--rhov-t) + - [Потенциалы](#потенциалы) + - [Условие применимости алгоритма Дейкстры для измененных длин ребер](#условие-применимости-алгоритма-дейкстры-для-измененных-длин-ребер) + - [Потенциал $\\pi(v) = \\rho(v, t)$](#потенциал-piv--rhov-t) + - [3.3. Алгоритм A\*. Условие монотонности на эвристику. Примеры эвристик](#33-алгоритм-a-условие-монотонности-на-эвристику-примеры-эвристик) + - [Алгоритм A\*](#алгоритм-a) + - [Примеры эвристик](#примеры-эвристик) + - [3.4. Алгоритм Форда-Беллмана. Хранение в матрице: $D\_{vk}$ равно длине кратчайшего пути до вершины $v$ за ровно $k$ ребер (не более $k$ ребер). Доказательство корректности (полное). Оценка времени работы](#34-алгоритм-форда-беллмана-хранение-в-матрице-d_vk-равно-длине-кратчайшего-пути-до-вершины-v-за-ровно-k-ребер-не-более-k-ребер-доказательство-корректности-полное-оценка-времени-работы) + - [Доказательство корректности(полное)](#доказательство-корректностиполное) + - [Оценка времени работы](#оценка-времени-работы-1) + - [3.5. Восстановление пути. Детектирование цикла отрицательного веса. Поиск самого цикла](#35-восстановление-пути-детектирование-цикла-отрицательного-веса-поиск-самого-цикла) + - [Восстановление пути](#восстановление-пути) + - [Детектирование цикла отрицательного веса](#детектирование-цикла-отрицательного-веса) + - [Поиск самого цикла](#поиск-самого-цикла) + - [3.6. Нахождение кратчайших путей с учетом циклов отрицательного веса](#36-нахождение-кратчайших-путей-с-учетом-циклов-отрицательного-веса) + - [3.7. Алгоритм Флойда. Доказательство (концепция). Восстановление пути](#37-алгоритм-флойда-доказательство-концепция-восстановление-пути) + - [Алгоритм Флойда](#алгоритм-флойда) + - [Доказательство (концепция)](#доказательство-концепция) + - [Восстановление пути](#восстановление-пути-1) + - [3.8. Нахождение цикла отрицательного веса](#38-нахождение-цикла-отрицательного-веса) + - [3.9. Алгоритм Джонсона. Добавление фиктивного корня и фиктивных ребер для запуска алгоритма Форда-Беллмана](#39-алгоритм-джонсона-добавление-фиктивного-корня-и-фиктивных-ребер-для-запуска-алгоритма-форда-беллмана) + - [Алгоритм Джонсона](#алгоритм-джонсона) + - [Добавление фиктивного корня и фиктивных ребер для запуска алгоритма Форда-Беллмана](#добавление-фиктивного-корня-и-фиктивных-ребер-для-запуска-алгоритма-форда-беллмана) +- [4. Остовные деревья](#4-остовные-деревья) + - [4.1. Остовное дерево. Построение с помощью обхода в глубину и в ширину](#41-остовное-дерево-построение-с-помощью-обхода-в-глубину-и-в-ширину) + - [Остовное дерево](#остовное-дерево) + - [Построение с помощью обхода в глубину и в ширину](#построение-с-помощью-обхода-в-глубину-и-в-ширину) + - [4.2. Определение минимального остовного дерева](#42-определение-минимального-остовного-дерева) + - [4.3. Теорема о разрезе. Доказательство](#43-теорема-о-разрезе-доказательство) + - [Теорема о разрезе](#теорема-о-разрезе) + - [4.4. Алгоритм Прима. Аналогия с алгоритмом Дейкстры. Оценка времени работы для различных реализаций очереди с приоритетом: бинарная куча, Фибоначчиева куча (последнее без доказательства)](#44-алгоритм-прима-аналогия-с-алгоритмом-дейкстры-оценка-времени-работы-для-различных-реализаций-очереди-с-приоритетом-бинарная-куча-фибоначчиева-куча-последнее-без-доказательства) + - [Алгоритм Прима](#алгоритм-прима) + - [Аналогия с алгоритмом Дейкстры](#аналогия-с-алгоритмом-дейкстры) + - [Оценка времени работы для различных реализаций очереди с приоритетом: бинарная куча, Фибоначчиева куча](#оценка-времени-работы-для-различных-реализаций-очереди-с-приоритетом-бинарная-куча-фибоначчиева-куча) + - [4.5. Алгоритм Прима. Доказательство корректности с помощью теоремы о разрезе](#45-алгоритм-прима-доказательство-корректности-с-помощью-теоремы-о-разрезе) + - [Доказательство корректности с помощью теоремы о разрезе](#доказательство-корректности-с-помощью-теоремы-о-разрезе) + - [4.6. Алгоритм Краскала. Доказательство корректности. Оценка времени работы](#46-алгоритм-краскала-доказательство-корректности-оценка-времени-работы) + - [Алгоритм Краскала](#алгоритм-краскала) + - [Доказательство корректности](#доказательство-корректности-1) + - [Оценка времени работы](#оценка-времени-работы-2) + - [4.7. Система непересекающихся множеств. Эвристика потенциалов без доказательства. Эвристика сжатия пути без доказательства. Почти константное время работы](#47-система-непересекающихся-множеств-эвристика-потенциалов-без-доказательства-эвристика-сжатия-пути-без-доказательства-почти-константное-время-работы) + - [4.8. Алгоритм Борувки. Доказательство(полное). Оценка времени работы](#48-алгоритм-борувки-доказательствополное-оценка-времени-работы) + - [Алгоритм Борувки](#алгоритм-борувки) + - [Оценка времени работы](#оценка-времени-работы-3) + - [4.9. Приближение решения задачи коммивояжера с помощью минимального остовного дерева](#49-приближение-решения-задачи-коммивояжера-с-помощью-минимального-остовного-дерева) +- [5. Потоки в сетях](#5-потоки-в-сетях) + - [5.1. Определение сети. Определение потока. Физический смысл. Аналогия с законами Кирхгофа. Определение разреза. Понятия потока через разрез](#51-определение-сети-определение-потока-физический-смысл-аналогия-с-законами-кирхгофа-определение-разреза-понятия-потока-через-разрез) + - [Определение сети](#определение-сети) + - [Определение потока](#определение-потока) + - [Физический смысл. Аналогия с законами Кирхгофа](#физический-смысл-аналогия-с-законами-кирхгофа) + - [Определение разреза](#определение-разреза) + - [Понятия потока через разрез](#понятия-потока-через-разрез) + - [5.2. Доказательство факта, что поток через любой разрез одинаковый](#52-доказательство-факта-что-поток-через-любой-разрез-одинаковый) + - [5.3. Понятие остаточной сети. Понятие дополняющего пути. Необходимость отсутствия дополняющего пути для максимальности потока](#53-понятие-остаточной-сети-понятие-дополняющего-пути-необходимость-отсутствия-дополняющего-пути-для-максимальности-потока) + - [Понятие остаточной сети](#понятие-остаточной-сети) + - [Понятие дополняющего пути](#понятие-дополняющего-пути) + - [Необходимость отсутствия дополняющего пути для максимальности потока](#необходимость-отсутствия-дополняющего-пути-для-максимальности-потока) + - [5.4. Теорема Форда-Фалкерсона](#54-теорема-форда-фалкерсона) + - [5.5. Алгоритм Форда-Фалкерсона. Поиск минимального разреза. Пример целочисленной сети, в котором алгоритм работает долго](#55-алгоритм-форда-фалкерсона-поиск-минимального-разреза-пример-целочисленной-сети-в-котором-алгоритм-работает-долго) + - [Алгоритм Форда-Фалкерсона](#алгоритм-форда-фалкерсона) + - [Поиск минимального разреза](#поиск-минимального-разреза) + - [5.6. Алгоритм Эдмондса-Карпа. Доказательство, что кратчайшее расстояние в остаточной сети не уменьшается](#56-алгоритм-эдмондса-карпа-доказательство-что-кратчайшее-расстояние-в-остаточной-сети-не-уменьшается) + - [Алгоритм Эдмондса-Карпа](#алгоритм-эдмондса-карпа) + - [5.7. Общая оценка времени работы алгоритма Эдмондса-Карпа](#57-общая-оценка-времени-работы-алгоритма-эдмондса-карпа) + - [5.8. Слоистая сеть. Алгоритм Диница. Оценка времени работы без доказательства](#58-слоистая-сеть-алгоритм-диница-оценка-времени-работы-без-доказательства) + - [Слоистая сеть](#слоистая-сеть) + - [Алгоритм Диница](#алгоритм-диница) + - [Оценка времени работы без доказательства](#оценка-времени-работы-без-доказательства) +- [6. RMQ. Sparse-table, дерево отрезков. LCA. Декартово дерево по неявному ключу](#6-rmq-sparse-table-дерево-отрезков-lca-декартово-дерево-по-неявному-ключу) + - [6.1. RSQ и RMQ. Sparse-table](#61-rsq-и-rmq-sparse-table) + - [RSQ и RMQ.](#rsq-и-rmq) + - [6.2. Дерево отрезков. Обработка запросов от листьев. Обработка запросов от корня](#62-дерево-отрезков-обработка-запросов-от-листьев-обработка-запросов-от-корня) + - [Дерево отрезков](#дерево-отрезков) + - [Обработка запросов от листьев. Обработка запросов от корня](#обработка-запросов-от-листьев-обработка-запросов-от-корня) + - [6.3. Изменение значения в массиве, обновление дерева отрезков. Множественные операции](#63-изменение-значения-в-массиве-обновление-дерева-отрезков-множественные-операции) + - [Изменение значения в массиве, обновление дерева отрезков.](#изменение-значения-в-массиве-обновление-дерева-отрезков) + - [Множественные операции](#множественные-операции) + - [6.4. LCA. Метод двоичного подъёма](#64-lca-метод-двоичного-подъёма) + - [LCA](#lca) + - [Метод двоичного подъёма](#метод-двоичного-подъёма) + - [6.5. Декартово дерево по неявному ключу. Интерфейс быстрого массива: Доступ к элементу в позиции i, Вставка элемента в позицию i, Удаление элемента из позиции i, Конкатенация двух массивов, разделение массива на два](#65-декартово-дерево-по-неявному-ключу-интерфейс-быстрого-массива-доступ-к-элементу-в-позиции-i-вставка-элемента-в-позицию-i-удаление-элемента-из-позиции-i-конкатенация-двух-массивов-разделение-массива-на-два) + - [Декартово дерево по неявному ключу](#декартово-дерево-по-неявному-ключу) + - [Интерфейс быстрого массива](#интерфейс-быстрого-массива) + - [Конкатенация двух массивов](#конкатенация-двух-массивов) + - [Разделение массива на два](#разделение-массива-на-два) + +
## 1. Обходы графа @@ -566,6 +567,8 @@ while (v != s) { Для поиска цикла на шаге 1 используем поиск в глубину. Сложность полученного алгоритма — $O(|E|)$, то есть линейная относительно количества рёбер в данном графе +
+ ## 2. Планарность графа ### 2.1. Формула Эйлера. Теорема Портнягина-Куратовского. Теорема Вагнера @@ -618,13 +621,15 @@ while (v != s) { Заметим, что на текущем шаге нет такого сегмента $S$ относительно $G_{k−1}$, для которого бы выполнялось равенство $Γ(S) = \varnothing$, так как в противном случае существовала бы цепь этого сегмента, контактные вершины которой принадлежали бы разным граням и укладка которой была бы невозможна. Следовательно, нельзя было бы уложить $S$,что противоречит тому, что $G$ — планарный граф. Значит, мы можем рассматривать только следующие два случая: 1. Существует сегмент $S$ для которого есть единственная вмещающая грань $Γ$, то есть $|Γ(S)| = 1$. Так как только грани $Γ$ принадлежат все контактные вершины $S$, то укладка этого сегмента в эту грань неизбежна. Это значит, что помещая любую цепь $L \subset S$, снова получим частичную укладку графа. - + 2. Для любого сегмента $S$ $|Γ(S)| \geq 2$. Построим граф $A(G'k−1)$, который по лемме 2 является двудольным. Рассмотрим его связную компоненту $K$, которая содержит не менее двух вершин. Граф $K$ также является двудольным. По лемме 1 для любого сегмента $S \in K$ справедливо $Γ(S)={Γ1, Γ2}$. Так как граф $K$ двудольный, то мы можем по очереди помещать сегменты $K$ в разные грани, причем конфликтующих сегментов не возникнет в силу четности всех циклов в графе. Результатом будет частичная укладка графа. Таким образом, на каждом шаге мы получаем частичную укладку графа, что доказывает корректность гамма-алгоритма. **Асимптотика алгоритма - $O(n^3)$.** +
+ ## 3. Кратчайшие пути во взвешенном графе ### 3.1. Алгоритм Дейкстры. Доказательство корректности(с доказательством). Оценка времени работы. Дерево кратчайших путей @@ -899,6 +904,8 @@ new_d[i][j] = min(d[i][j], d[i][k] + d[k][j]); Как в случае применения алгоритма Беллмана-Форда для обнаружения отрицательных циклов, существуют два способа сделать вес каждого ребра неотрицательным в произвольной сети без отрицательных циклов. Либо можно начать с истока в каждом сильно связном компоненте, либо добавить фиктивную вершину с ребром нулевой длины в каждую вершину сети. В любом случае получится остовный лес кратчайших путей, которым можно воспользоваться для присвоения весов вершинам (вес пути из корня в данную вершину в ее SPT). +
+ ## 4. Остовные деревья ### 4.1. Остовное дерево. Построение с помощью обхода в глубину и в ширину @@ -1047,6 +1054,8 @@ $O(ElogE)$. 5. Образуем эйлеров цикл в $H$. 6. Преобразуем цикл, найденный на предыдущем шаге, в гамильтонов цикл путём пропуска повторяющихся вершин (сокращение). +
+ ## 5. Потоки в сетях ### 5.1. Определение сети. Определение потока. Физический смысл. Аналогия с законами Кирхгофа. Определение разреза. Понятия потока через разрез @@ -1181,6 +1190,8 @@ $level_{i+1}[w] >= level_i[w] + 2$. Так как расстояние строго увеличивается количество итераций алгоритма = $O(V)$. В зависимости от выбора алгоритма поиска блокирующего потока время данного алгоритма может составить $O(VE^2), O(V^2E)$ или $O(V^3)$. +
+ ## 6. RMQ. Sparse-table, дерево отрезков. LCA. Декартово дерево по неявному ключу ### 6.1. RSQ и RMQ. Sparse-table From 7820c7ddc71e907825acced8b2591f4ee103ed2f Mon Sep 17 00:00:00 2001 From: Egor Ivanov <56685477+sibenshtern@users.noreply.github.com> Date: Sun, 14 Jan 2024 10:25:55 +0000 Subject: [PATCH 6/7] update theory and upload pdf file --- doc/theory.md | 1350 ++++++++++++++++++++++++++++++++++++++++++++++++ doc/theory.pdf | Bin 0 -> 387856 bytes 2 files changed, 1350 insertions(+) create mode 100644 doc/theory.md create mode 100644 doc/theory.pdf diff --git a/doc/theory.md b/doc/theory.md new file mode 100644 index 0000000..8a6bd92 --- /dev/null +++ b/doc/theory.md @@ -0,0 +1,1350 @@ +# Теория по алгоритмам + +- [1. Обходы графа](#1-обходы-графа) + - [1.1. Ориентированный граф, псевдограф. Неориентированный граф, псевдограф. Связность в неориентированном графе, компоненты связности. Слабая и сильная связность в ориентированном графе. Компоненты слабой, сильной связности](#11-ориентированный-граф-псевдограф-неориентированный-граф-псевдограф-связность-в-неориентированном-графе-компоненты-связности-слабая-и-сильная-связность-в-ориентированном-графе-компоненты-слабой-сильной-связности) + - [Ориентированный граф](#ориентированный-граф) + - [Неориентированный граф](#неориентированный-граф) + - [1.2. Обход в глубину. Цвета вершин. Времена входа и выхода. Лемма о белых путях (с доказательством)](#12-обход-в-глубину-цвета-вершин-времена-входа-и-выхода-лемма-о-белых-путях-с-доказательством) + - [Обход в глубину](#обход-в-глубину) + - [Лемма о белых путях](#лемма-о-белых-путях) + - [1.3. Проверка связности неориентированного графа. Поиск цикла в неориентированном и ориентированном графе. Топологическая сортировка](#13-проверка-связности-неориентированного-графа-поиск-цикла-в-неориентированном-и-ориентированном-графе-топологическая-сортировка) + - [Проверка связности неориентированного графа](#проверка-связности-неориентированного-графа) + - [Поиск цикла в неориентированном и ориентированном графе](#поиск-цикла-в-неориентированном-и-ориентированном-графе) + - [Топологическая сортировка](#топологическая-сортировка) + - [1.4. Нахождение компонент сильной связности. Алгоритм Косарайю с доказательством корректности(концепция). Алгоритм Тарьяна без доказательства корректности](#14-нахождение-компонент-сильной-связности-алгоритм-косарайю-с-доказательством-корректностиконцепция-алгоритм-тарьяна-без-доказательства-корректности) + - [Нахождение компонент сильной связности](#нахождение-компонент-сильной-связности) + - [Алгоритм Косарайю с доказательством корректности](#алгоритм-косарайю-с-доказательством-корректности) + - [Алгоритм Тарьяна без доказательства корректности](#алгоритм-тарьяна-без-доказательства-корректности) + - [1.5. Компоненты реберной двусвязности. Мосты. Поиск мостов](#15-компоненты-реберной-двусвязности-мосты-поиск-мостов) + - [Компоненты реберной двусвязности](#компоненты-реберной-двусвязности) + - [Мосты](#мосты) + - [Поиск мостов](#поиск-мостов) + - [1.6. Компоненты вершинной двусвязности. Точки сочленения. Поиск точек сочленения](#16-компоненты-вершинной-двусвязности-точки-сочленения-поиск-точек-сочленения) + - [Компоненты вершинной двусвязности](#компоненты-вершинной-двусвязности) + - [Точки сочленения](#точки-сочленения) + - [Поиск точек сочленения](#поиск-точек-сочленения) + - [1.7. Волновой алгоритм. Обход в ширину (применение очереди в волновом алгоритме)](#17-волновой-алгоритм-обход-в-ширину-применение-очереди-в-волновом-алгоритме) + - [Волновой алгоритм](#волновой-алгоритм) + - [Обход в ширину (применение очереди в волновом алгоритме)](#обход-в-ширину-применение-очереди-в-волновом-алгоритме) + - [1.8. Критерий существования Эйлерова пути и цикла в ориентированном и неориентированном графе. Поиск эйлерова пути и цикла](#18-критерий-существования-эйлерова-пути-и-цикла-в-ориентированном-и-неориентированном-графе-поиск-эйлерова-пути-и-цикла) + - [Критерий существования Эйлерова пути и цикла в неориентированном графе](#критерий-существования-эйлерова-пути-и-цикла-в-неориентированном-графе) + - [Критерий существования Эйлерова пути и цикла в ориентированном графе](#критерий-существования-эйлерова-пути-и-цикла-в-ориентированном-графе) + - [Поиск эйлерова пути и цикла](#поиск-эйлерова-пути-и-цикла) +- [2. Планарность графа](#2-планарность-графа) + - [2.1. Формула Эйлера. Теорема Портнягина-Куратовского. Теорема Вагнера](#21-формула-эйлера-теорема-портнягина-куратовского-теорема-вагнера) + - [Формула Эйлера](#формула-эйлера) + - [Теорема Портнягина-Куратовского](#теорема-портнягина-куратовского) + - [Теорема Вагнера](#теорема-вагнера) + - [2.2. Гамма алгоритм. Контактная вершина](#22-гамма-алгоритм-контактная-вершина) + - [Гамма алгоритм](#гамма-алгоритм) + - [2.3. Теорема о корректности Гамма алгоритма. Асимптотика алгоритма](#23-теорема-о-корректности-гамма-алгоритма-асимптотика-алгоритма) + - [Теорема о корректности Гамма алгоритма](#теорема-о-корректности-гамма-алгоритма) +- [3. Кратчайшие пути во взвешенном графе](#3-кратчайшие-пути-во-взвешенном-графе) + - [3.1. Алгоритм Дейкстры. Доказательство корректности(с доказательством). Оценка времени работы. Дерево кратчайших путей](#31-алгоритм-дейкстры-доказательство-корректностис-доказательством-оценка-времени-работы-дерево-кратчайших-путей) + - [Алгоритм Дейкстры](#алгоритм-дейкстры) + - [Доказательство корректности](#доказательство-корректности) + - [Оценка времени работы](#оценка-времени-работы) + - [Дерево кратчайших путей](#дерево-кратчайших-путей) + - [3.2. Потенциалы. Условие применимости алгоритма Дейкстры для измененных длин ребер. Потенциал $\\pi(v) = \\rho(v, t)$](#32-потенциалы-условие-применимости-алгоритма-дейкстры-для-измененных-длин-ребер-потенциал-piv--rhov-t) + - [Потенциалы](#потенциалы) + - [Условие применимости алгоритма Дейкстры для измененных длин ребер](#условие-применимости-алгоритма-дейкстры-для-измененных-длин-ребер) + - [Потенциал $\\pi(v) = \\rho(v, t)$](#потенциал-piv--rhov-t) + - [3.3. Алгоритм A\*. Условие монотонности на эвристику. Примеры эвристик](#33-алгоритм-a-условие-монотонности-на-эвристику-примеры-эвристик) + - [Алгоритм A\*](#алгоритм-a) + - [Примеры эвристик](#примеры-эвристик) + - [3.4. Алгоритм Форда-Беллмана. Хранение в матрице: $D\_{vk}$ равно длине кратчайшего пути до вершины $v$ за ровно $k$ ребер (не более $k$ ребер). Доказательство корректности (полное). Оценка времени работы](#34-алгоритм-форда-беллмана-хранение-в-матрице-d_vk-равно-длине-кратчайшего-пути-до-вершины-v-за-ровно-k-ребер-не-более-k-ребер-доказательство-корректности-полное-оценка-времени-работы) + - [Доказательство корректности(полное)](#доказательство-корректностиполное) + - [Оценка времени работы](#оценка-времени-работы-1) + - [3.5. Восстановление пути. Детектирование цикла отрицательного веса. Поиск самого цикла](#35-восстановление-пути-детектирование-цикла-отрицательного-веса-поиск-самого-цикла) + - [Восстановление пути](#восстановление-пути) + - [Детектирование цикла отрицательного веса](#детектирование-цикла-отрицательного-веса) + - [Поиск самого цикла](#поиск-самого-цикла) + - [3.6. Нахождение кратчайших путей с учетом циклов отрицательного веса](#36-нахождение-кратчайших-путей-с-учетом-циклов-отрицательного-веса) + - [3.7. Алгоритм Флойда. Доказательство (концепция). Восстановление пути](#37-алгоритм-флойда-доказательство-концепция-восстановление-пути) + - [Алгоритм Флойда](#алгоритм-флойда) + - [Доказательство (концепция)](#доказательство-концепция) + - [Восстановление пути](#восстановление-пути-1) + - [3.8. Нахождение цикла отрицательного веса](#38-нахождение-цикла-отрицательного-веса) + - [3.9. Алгоритм Джонсона. Добавление фиктивного корня и фиктивных ребер для запуска алгоритма Форда-Беллмана](#39-алгоритм-джонсона-добавление-фиктивного-корня-и-фиктивных-ребер-для-запуска-алгоритма-форда-беллмана) + - [Алгоритм Джонсона](#алгоритм-джонсона) + - [Добавление фиктивного корня и фиктивных ребер для запуска алгоритма Форда-Беллмана](#добавление-фиктивного-корня-и-фиктивных-ребер-для-запуска-алгоритма-форда-беллмана) +- [4. Остовные деревья](#4-остовные-деревья) + - [4.1. Остовное дерево. Построение с помощью обхода в глубину и в ширину](#41-остовное-дерево-построение-с-помощью-обхода-в-глубину-и-в-ширину) + - [Остовное дерево](#остовное-дерево) + - [Построение с помощью обхода в глубину и в ширину](#построение-с-помощью-обхода-в-глубину-и-в-ширину) + - [4.2. Определение минимального остовного дерева](#42-определение-минимального-остовного-дерева) + - [4.3. Теорема о разрезе. Доказательство](#43-теорема-о-разрезе-доказательство) + - [Теорема о разрезе](#теорема-о-разрезе) + - [4.4. Алгоритм Прима. Аналогия с алгоритмом Дейкстры. Оценка времени работы для различных реализаций очереди с приоритетом: бинарная куча, Фибоначчиева куча (последнее без доказательства)](#44-алгоритм-прима-аналогия-с-алгоритмом-дейкстры-оценка-времени-работы-для-различных-реализаций-очереди-с-приоритетом-бинарная-куча-фибоначчиева-куча-последнее-без-доказательства) + - [Алгоритм Прима](#алгоритм-прима) + - [Аналогия с алгоритмом Дейкстры](#аналогия-с-алгоритмом-дейкстры) + - [Оценка времени работы для различных реализаций очереди с приоритетом: бинарная куча, Фибоначчиева куча](#оценка-времени-работы-для-различных-реализаций-очереди-с-приоритетом-бинарная-куча-фибоначчиева-куча) + - [4.5. Алгоритм Прима. Доказательство корректности с помощью теоремы о разрезе](#45-алгоритм-прима-доказательство-корректности-с-помощью-теоремы-о-разрезе) + - [Доказательство корректности с помощью теоремы о разрезе](#доказательство-корректности-с-помощью-теоремы-о-разрезе) + - [4.6. Алгоритм Краскала. Доказательство корректности. Оценка времени работы](#46-алгоритм-краскала-доказательство-корректности-оценка-времени-работы) + - [Алгоритм Краскала](#алгоритм-краскала) + - [4.7. Система непересекающихся множеств. Эвристика потенциалов без доказательства. Эвристика сжатия пути без доказательства. Почти константное время работы](#47-система-непересекающихся-множеств-эвристика-потенциалов-без-доказательства-эвристика-сжатия-пути-без-доказательства-почти-константное-время-работы) + - [4.8. Алгоритм Борувки. Доказательство. Оценка времени работы](#48-алгоритм-борувки-доказательство-оценка-времени-работы) + - [Алгоритм Борувки](#алгоритм-борувки) + - [Оценка времени работы](#оценка-времени-работы-2) + - [4.9. Приближение решения задачи коммивояжера с помощью минимального остовного дерева](#49-приближение-решения-задачи-коммивояжера-с-помощью-минимального-остовного-дерева) +- [5. Потоки в сетях](#5-потоки-в-сетях) + - [5.1. Определение сети. Определение потока. Физический смысл. Аналогия с законами Кирхгофа. Определение разреза. Понятия потока через разрез](#51-определение-сети-определение-потока-физический-смысл-аналогия-с-законами-кирхгофа-определение-разреза-понятия-потока-через-разрез) + - [Определение сети](#определение-сети) + - [Определение потока](#определение-потока) + - [Физический смысл. Аналогия с законами Кирхгофа](#физический-смысл-аналогия-с-законами-кирхгофа) + - [Определение разреза](#определение-разреза) + - [Понятия потока через разрез](#понятия-потока-через-разрез) + - [5.2. Доказательство факта, что поток через любой разрез одинаковый](#52-доказательство-факта-что-поток-через-любой-разрез-одинаковый) + - [5.3. Понятие остаточной сети. Понятие дополняющего пути. Необходимость отсутствия дополняющего пути для максимальности потока](#53-понятие-остаточной-сети-понятие-дополняющего-пути-необходимость-отсутствия-дополняющего-пути-для-максимальности-потока) + - [Понятие остаточной сети](#понятие-остаточной-сети) + - [Понятие дополняющего пути](#понятие-дополняющего-пути) + - [Необходимость отсутствия дополняющего пути для максимальности потока](#необходимость-отсутствия-дополняющего-пути-для-максимальности-потока) + - [5.4. Теорема Форда-Фалкерсона](#54-теорема-форда-фалкерсона) + - [5.5. Алгоритм Форда-Фалкерсона. Поиск минимального разреза. Пример целочисленной сети, в котором алгоритм работает долго](#55-алгоритм-форда-фалкерсона-поиск-минимального-разреза-пример-целочисленной-сети-в-котором-алгоритм-работает-долго) + - [Алгоритм Форда-Фалкерсона](#алгоритм-форда-фалкерсона) + - [Поиск минимального разреза](#поиск-минимального-разреза) + - [5.6. Алгоритм Эдмондса-Карпа. Доказательство, что кратчайшее расстояние в остаточной сети не уменьшается](#56-алгоритм-эдмондса-карпа-доказательство-что-кратчайшее-расстояние-в-остаточной-сети-не-уменьшается) + - [Алгоритм Эдмондса-Карпа](#алгоритм-эдмондса-карпа) + - [5.7. Общая оценка времени работы алгоритма Эдмондса-Карпа](#57-общая-оценка-времени-работы-алгоритма-эдмондса-карпа) + - [5.8. Слоистая сеть. Алгоритм Диница. Оценка времени работы без доказательства](#58-слоистая-сеть-алгоритм-диница-оценка-времени-работы-без-доказательства) + - [Слоистая сеть](#слоистая-сеть) + - [Алгоритм Диница](#алгоритм-диница) + - [Оценка времени работы без доказательства](#оценка-времени-работы-без-доказательства) +- [6. RMQ. Sparse-table, дерево отрезков. LCA. Декартово дерево по неявному ключу](#6-rmq-sparse-table-дерево-отрезков-lca-декартово-дерево-по-неявному-ключу) + - [6.1. RSQ и RMQ. Sparse-table](#61-rsq-и-rmq-sparse-table) + - [RSQ и RMQ](#rsq-и-rmq) + - [6.2. Дерево отрезков. Обработка запросов от листьев. Обработка запросов от корня](#62-дерево-отрезков-обработка-запросов-от-листьев-обработка-запросов-от-корня) + - [Дерево отрезков](#дерево-отрезков) + - [Обработка запросов от листьев. Обработка запросов от корня](#обработка-запросов-от-листьев-обработка-запросов-от-корня) + - [6.3. Изменение значения в массиве, обновление дерева отрезков. Множественные операции](#63-изменение-значения-в-массиве-обновление-дерева-отрезков-множественные-операции) + - [Изменение значения в массиве, обновление дерева отрезков.](#изменение-значения-в-массиве-обновление-дерева-отрезков) + - [Множественные операции](#множественные-операции) + - [6.4. LCA. Метод двоичного подъёма](#64-lca-метод-двоичного-подъёма) + - [LCA](#lca) + - [Метод двоичного подъёма](#метод-двоичного-подъёма) + - [6.5. Декартово дерево по неявному ключу. Интерфейс быстрого массива: Доступ к элементу в позиции i, Вставка элемента в позицию i, Удаление элемента из позиции i, Конкатенация двух массивов, разделение массива на два](#65-декартово-дерево-по-неявному-ключу-интерфейс-быстрого-массива-доступ-к-элементу-в-позиции-i-вставка-элемента-в-позицию-i-удаление-элемента-из-позиции-i-конкатенация-двух-массивов-разделение-массива-на-два) + - [Декартово дерево по неявному ключу](#декартово-дерево-по-неявному-ключу) + - [Интерфейс быстрого массива](#интерфейс-быстрого-массива) + - [Конкатенация двух массивов](#конкатенация-двух-массивов) + - [Разделение массива на два](#разделение-массива-на-два) + + +## 1. Обходы графа + +### 1.1. Ориентированный граф, псевдограф. Неориентированный граф, псевдограф. Связность в неориентированном графе, компоненты связности. Слабая и сильная связность в ориентированном графе. Компоненты слабой, сильной связности + +#### Ориентированный граф + +**Ориентированным графом** (англ. *directed graph*) $G$ называется пара $G = (V, E)$, где $V$ — множество вершин (англ. vertices), а $E \subset V \times V$ — множество рёбер. + +**Псевдограф (*мультиорграф*)** — это ориентированный граф, в котором разрешены кратные дуги, то есть дуги, имеющие те же начальные и конечные вершины. + +**Псевдографом** $G$ называется упорядоченная пара $G := (V, A)$, в которой $V$ — множество вершин, $A$ — мультимножество упорядоченных пар вершин. Элементы этого множества называются дугами. + +Ориентированный граф называется **слабо связным**, если соответствующий неориентированный граф является связным; + +Ориентированный граф называется **сильно связным**, если всякая вершина $v$ достижима из любой другой вершины $v$; + +**Компонентой сильной связности** ориентированного графа называется максимальный по включению сильно связный подграф. Другими словами, это подграф, любые две вершины которого принадлежат какому-либо циклу, и содержащий все такие циклы для своих вершин. + +**Компонентой слабой связности** ориентированного графа называют его максимальный слабо связный подграф. + +#### Неориентированный граф + +**Неориентированным графом** (англ. *undirected graph*) $G$ называется пара $G = (V, E)$, где $V$ — множество вершин, а $E \subset {{v,u} : v,u \in V}$ — множество рёбер. + +Формально, **псевдографом** (***мультифграфом***) $G$ называется упорядоченная пара $G := (V, E)$, в которой $V$ — множество вершин, $E$ — мультимножество неупорядоченных пар вершин. Элементы этого множества называются рёбрами. + +Неориентированный граф называется **связным**, если все его вершины достижимы из некоторой вершины (эквивалентно, из любой его вершины). + +**Компонентой связности** неориентированного графа называется максимальный по включению связный подграф + +### 1.2. Обход в глубину. Цвета вершин. Времена входа и выхода. Лемма о белых путях (с доказательством) + +#### Обход в глубину + +**Обход в глубину** (поиск в глубину, англ. *Depth-First Search*, *DFS*) — один из основных методов обхода графа, часто используемый для проверки связности, поиска цикла и компонент сильной связности и для топологической сортировки. + +```c++ +#include + +std::vector> graph; // граф +int vertex_count; // число вершин + +std::vector color; // цвет вершины (0, 1, или 2) + +std::vector time_in, time_out; // "времена" захода и выхода из вершины +int dfs_timer = 0; // "таймер" для определения времён + +void dfs (int vertex) { + time_in[vertex] = dfs_timer++; + color[vertex] = 1; + for (int u : graph[vertex]) + if (color[u] == 0) + dfs(u); + color[v] = 2; + time_out[v] = dfs_timer++; +} +``` + + +#### Лемма о белых путях + +**Лемма**: Не существует такого момента выполнения поиска в глубину, в который бы существовало ребро из черной вершины в белую. + +**Доказательство**: Пусть в процессе выполнения процедуры DFS нашлось ребро из черной вершины $v$ в белую вершину u. Рассмотрим момент времени, когда мы запустили DFS(v). В этот момент вершина $v$ была перекрашена из белого в серый, а вершина u была белая. Далее в ходе выполнения алгоритма будет запущен DFS(u), поскольку обход в глубину обязан посетить все белые вершины, в которые есть ребро из v. По алгоритму вершина $v$ будет покрашена в черный цвет тогда, когда завершится обход всех вершин, достижимых из нее по одному ребру, кроме тех, что были рассмотрены раньше нее. Таким образом, вершина $v$ может стать черной только тогда, когда DFS выйдет из вершины u, и она будет покрашена в черный цвет. Получаем противоречие. + +### 1.3. Проверка связности неориентированного графа. Поиск цикла в неориентированном и ориентированном графе. Топологическая сортировка + +#### Проверка связности неориентированного графа + +Небольшая модификация алгоритма обхода в глубину, в которой будем возвращать количество посещенных вершин. Запустим такой **DFS** от некоторой вершины графа $G$, если его результат равен $|V|$, то мы побывали во всех вершинах графа, а следовательно он связен, иначе какие-то вершины остались не посещёнными. Работает алгоритм за $O(|V|+|E|)$ + +```c++ +std::vector> graph; // граф + +// visited — массив цветов вершин +int dfs(int vertex, std::vector visited): + int visitedVertices = 1 + visited[vertex] = true // помечаем вершину как пройденную + for (auto u : graph[vertex]) // проходим по смежным с u вершинам + // проверяем, не находились ли мы ранее в выбранной вершине + if (!visited[v]) + visitedVertices += dfs(v, visited) + return visitedVertices +``` + + +#### Поиск цикла в неориентированном и ориентированном графе + +*Условие:* + +Пусть дан ориентированный или неориентированный граф без петель и кратных рёбер. Требуется проверить, является ли он ациклическим, а если не является, то найти любой цикл. + +*Решение:* + +Произведём серию поисков в глубину в графе. Т.е. из каждой вершины, в которую мы ещё ни разу не приходили, запустим поиск в глубину, который при входе в вершину будет красить её в серый цвет, а при выходе - в чёрный. И если поиск в глубину пытается пойти в серую вершину, то это означает, что мы нашли цикл (если граф неориентированный, то случаи, когда поиск в глубину из какой-то вершины пытается пойти в предка, не считаются). + +```c++ +#include +#include + +int n; +std::vector> graph; +std::vector color; +std::vector parent; +int cycle_start, cycle_end; + +bool dfs(int vertex) { + color[vertex] = 1; + for (size_t i = 0; i < graph[vertex].size(); ++i) { + int to = graph[vertex][i]; + if (color[to] == 0) { + parent[to] = vertex; + if (dfs(to)) return true; + } + else if (color[to] == 1) { + cycle_end = vertex; + cycle_start = to; + return true; + } + } + color[vertex] = 2; + return false; +} + +int main() { + // чтение графа + + parent.assign(n, -1); + color.assign(n, 0); + cycle_start = -1; + for (int i = 0; i < n; ++i) + if (dfs(i)) + break; + + if (cycle_st == -1) + std::cout << "Acyclic" << "\n"; + else { + std::cout << "Cyclic" << "\n"; + std::vector cycle; + cycle.push_back(cycle_start); + for (int vertex = cycle_end; vertex != cycle_start; vertex = parent[vertex]) + cycle.push_back(vertex); + cycle.push_back(cycle_start); + reverse(cycle.begin(), cycle.end()); + for (size_t i = 0; i < cycle.size(); ++i) + std::cout << cycle[i]+1 << " "; + } +} +``` + +#### Топологическая сортировка + +**Топологическая сортировка** - упорядочивание вершин ациклического ориентированного графа согласно частичному порядку, заданному ребрами орграфа на множестве его вершин. + +*Условие:* + +Дан ориентированный граф с $n$ вершинами и $m$ рёбрами. Требуется перенумеровать его вершины таким образом, чтобы каждое ребро вело из вершины с меньшим номером в вершину с большим. Иными словами, требуется найти перестановку вершин (топологический порядок), соответствующую порядку, задаваемому всеми рёбрами графа. + +Топологическая сортировка может быть не единственной (например, если граф — пустой; или если есть три такие вершины $a$, $b$, $c$, что из $a$ есть пути в $b$ и в $c$, но ни из $b$ в $c$, ни из $c$ в $b$ добраться нельзя). + +Топологической сортировки может не существовать вовсе — если граф содержит циклы (поскольку при этом возникает противоречие: есть путь и из одной вершины в другую, и наоборот). + +*Решение:* + +```c++ +#include + +int n; // число вершин +std::vector> graph; // граф +std::vector visited; +std::vector topological_sort; + +void dfs (int vertex) { + visited[vertex] = true; + for (int u : graph[vertex]) + if (!visited[u] == 0) + dfs(u); + topological_sort.push_back(vertex); +} + +void topological_sort() { + visited.assign(n, false); + answer.clear(); + for (int i = 0; i < n; ++i) + if (!visited[i]) + dfs(i); + reverse(answer.begin(), answer.end()); +} +``` + +### 1.4. Нахождение компонент сильной связности. Алгоритм Косарайю с доказательством корректности(концепция). Алгоритм Тарьяна без доказательства корректности + +#### Нахождение компонент сильной связности + +**Компонента сильной связанности** (англ. *strongly connected component*) - такое (максимальное по включению) подмножество вершин $C$, что любые две вершины этого подмножества достижимы друг из друга, т.е. для любых $u$, $v \in C: u \rightarrow v, v \rightarrow u$, где символом $\rightarrow$ здесь и далее мы будем обозначать достижимость, т.е. существование пути из первой вершины во вторую. + +*Условие:* + +Дан ориентированный граф $G$, множество вершин которого $V$ и множество рёбер — $E$. Петли и кратные рёбра допускаются. Обозначим через $n$ количество вершин графа, через $m$ — количество рёбер. + +*Решение:* + +Понятно, что компоненты сильной связности для данного графа не пересекаются, т.е. фактически это разбиение всех вершин графа. Отсюда логично определение конденсации G как графа, получаемого из данного графа сжатием каждой компоненты сильной связности в одну вершину. Каждой вершине графа конденсации соответствует компонента сильной связности графа G, а ориентированное ребро между двумя вершинами $C_i$ и $C_j$ графа конденсации проводится, если найдётся пара вершин u из $C_i$, из $C_j$, между которыми существовало ребро в исходном графе, т.е. $(u, v)$ из $E$. + +Важнейшим свойством графа конденсации является то, что он ацикличен. Действительно, предположим, что $C \rightarrow C'$, докажем, что $C' \mapsto C$. Из определения конденсации получаем, что найдутся две вершины $u$ из $C$ и $v$ из $C'$, что $u \rightarrow v$. Доказывать будем от противного, т.е. предположим, что $C' \rightarrow C$, тогда найдутся две вершины $u'$ из $C$ и $v'$ из $C'$, что $v' \rightarrow u'$. Но т.к. $u$ и $u'$ находятся в одной компоненте сильной связности, то между ними есть путь; аналогично для $v$ и $v'$. В итоге, объединяя пути, получаем, что $v \rightarrow u$, и одновременно $u \rightarrow v$. Следовательно, $u$ и $v$ должны принадлежать одной компоненте сильной связности, т.е. получили противоречие, что и требовалось доказать + +#### Алгоритм Косарайю с доказательством корректности + +**Алгоритм Косарайю** предназначен для поиска компонент сильной связности в ориентированном графе и состоит из трёх шагов: + +1. Выполнить поиск в глубину (DFS), пока не будут «помечены» все вершины. Вершина считается «помеченной», когда ей присвоено время выхода из рекурсии (см. основные понятия). +2. Инвертировать исходный граф +3. Выполнить DFS в порядке убывания пометок вершин. + +Полученные деревья каждого такта DFS последнего шага являются компонентами сильной связности + +*Условие:* + +Вершины $u$ и $v$ сильно связаны $\Leftrightarrow$ после выполнения алгоритма они принадлежат одному дереву такта DFS. + +*Доказательство корректности алгоритма:* + +Если вершины u и $v$ были сильно связаны в графе $G$, на третьем этапе будет найден путь из одной вершины в другую (по Лемме (Инвертирование рёбер цикла не влияет на его цикличность)), так как на первом шаге был найден путь $u \rightarrow v$, а на третьем – путь $v \rightarrow u$. Это означает, что по окончании алгоритма обе вершины лежат в одном дереве. + +Вершины $u$ и $v$ лежат в одном и том же дереве поиска в глубину на третьем шаге алгоритма. Значит, они обе достижимы из корня $r$ этого дерева. + +Вершина $r$ была рассмотрена на 3 шаге раньше всех, значит время выхода из неё на 1 шаге больше, чем время выхода из вершин $u$ и $v$. Из этого мы получаем 2 случая: + +Обе эти вершины были достижимы из $r$ в исходном графе. Это означает сильную связность вершин $u$ и $r$ и сильную связность вершин $v$ и $r$. Склеивая пути мы получаем связность вершин $u$ и $v$ + +Хотя бы одна вершина не достижима из $r$ в исходном графе, например $v$. Значит и $r$ была не достижима из $v$ в исходном графе, так как время выхода из $r$ — больше (если бы она была достижима, то время выхода из $v$ было бы больше, чем из $r$). Значит между этими вершинами нет пути (ни в исходном, ни в инвертированном графах), но последнего быть не может, потому что по условию $v$ достижима из $r$ на 3 шаге (в инвертированном графе) + +Значит, из случая 1 и не существования случая 2 получаем, что вершины $u$ и $v$ сильно связаны в обоих графах + +#### Алгоритм Тарьяна без доказательства корректности + +Основой для алгоритма является структура данных "Система непересекающихся множеств", которая и была изобретена Тарьяном (*Tarjan*). + +Алгоритм фактически представляет собой обход в глубину из корня дерева, в процессе которого постепенно находятся ответы на запросы. А именно, ответ на запрос $(v, u)$ находится, когда обход в глубину находится в вершине $u$, а вершина $v$ уже была посещена, или наоборот. + +Итак, пусть обход в глубину находится в вершине $v$ (и уже были выполнены переходы в её сыновей), и оказалось, что для какого-то запроса $(v, u)$ вершина u уже была посещена обходом в глубину. Научимся тогда находить $LCA$ этих двух вершин. + +Заметим, что $LCA(v, u)$ является либо самой вершиной $v$, либо одним из её предков. Получается, нам надо найти самую нижнюю вершину среди предков $v$ (включая её саму), для которой вершина u является потомком. Заметим, что при фиксированном $v$ по такому признаку (т.е. какой наименьший предок $v$ является и предком какой-то вершины) вершины дерева дерева распадаются на совокупность непересекающихся классов. Для каждого предка $p \neq v$ вершины $v$ её класс содержит саму эту вершину, а также все поддеревья с корнями в тех её сыновьях, которые лежат "слева" от пути до $v$ (т.е. которые были обработаны ранее, чем была достигнута $v$). + +Нам надо научиться эффективно поддерживать все эти классы, для чего мы и применим структуру данных "Система непересекающихся множеств". Каждому классу будет соответствовать в этой структуре множество, причём для представителя этого множества мы определим величину **ANCESTOR** — ту вершину $p$, которая и образует этот класс. + +Рассмотрим подробно реализацию обхода в глубину. Пусть мы стоим в некоторой вершине $v$. Поместим её в отдельный класс в структуре непересекающихся множеств, **ANCESTOR**[v] = $v$. Как обычно в обходе в глубину, перебираем все исходящие рёбра \$(v, $to$ )\$. Для каждого такого $to$ мы сначала должны вызвать обход в глубину из этой вершины, а потом добавить эту вершину со всем её поддеревом в класс вершины $v$. Это реализуется операцией **Union** структуры данных "система непересекающихся множеств", с последующей установкой **ANCESTOR** = $v$ для представителя множества (т.к. после объединения представитель класса мог измениться). Наконец, после обработки всех рёбер мы перебираем все запросы вида $(v, u)$, и если u была помечена как посещённая обходом в глубину, то ответом на этот запрос будет вершина $LCA(v,u)$ = **ANCESTOR**[FindSet(u)]. Нетрудно заметить, что для каждого запроса это условие (что одна вершина запроса является текущей, а другая была посещена ранее) выполнится ровно один раз. + +Оценим асимптотику. Она складывается из нескольких частей. Во-первых, это асимптотика обхода в глубину, которая в данном случае составляет $O(n)$. Во-вторых, это операции по объединению множеств, которые в сумме для всех разумных n затрачивают $O(n)$ операций. В-третьих, это для каждого запроса проверка условия (два раза на запрос) и определение результата (один раз на запрос), каждое, опять же, для всех разумных n выполняется за $O(1)$. Итоговая асимптотика получается $O(n + m)$, что означает для достаточно больших $m$ $(n = O(m))$ ответ за $O(1)$ на один запрос + +### 1.5. Компоненты реберной двусвязности. Мосты. Поиск мостов + +#### Компоненты реберной двусвязности + +**Компонентами рёберной двусвязности** (англ. *costal doubly-linked components*) графа называют его подграфы, множества вершин которых - классы эквивалентности рёберной двусвязности, а множества рёбер - множества ребер из соответствующих классов эквивалентности. + +#### Мосты + +**Мостом** называется такое ребро, удаление которого делает граф несвязным (или, точнее, увеличивает число компонент связности). + +#### Поиск мостов + +*Алгоритм:* + +Запустим обход в глубину из произвольной вершины графа; обозначим её через **root**. Заметим следующий факт (который несложно доказать): + +Пусть мы находимся в обходе в глубину, просматривая сейчас все рёбра из вершины $v$. Тогда, если текущее ребро $(v,to)$ таково, что из вершины $to$ и из любого её потомка в дереве обхода в глубину нет обратного ребра в вершину $v$ или какого-либо её предка, то это ребро является мостом. В противном случае оно мостом не является. (В самом деле, мы этим условием проверяем, нет ли другого пути из $v$ в $to$ , кроме как спуск по ребру $(v,to)$ дерева обхода в глубину.) + +Теперь осталось научиться проверять этот факт для каждой вершины эффективно. Для этого воспользуемся "временами входа в вершину", вычисляемыми алгоритмом поиска в глубину. + +Итак, пусть $tin[v]$ — это время захода поиска в глубину в вершину $v$. Теперь введём массив $fup[v]$, который и позволит нам отвечать на вышеописанные запросы. Время $fup[v]$ равно минимуму из времени захода в саму вершину $tin[v]$, времён захода в каждую вершину $p$, являющуюся концом некоторого обратного ребра $(v, p)$, а также из всех значений $fup[to]$ для каждой вершины $to$ , являющейся непосредственным сыном $v$ в дереве поиска: + +$fup[v] = \min(tin[v], tin[p], fup(to))$ $(v, p)$ - back edge, $(v, to)$ - tree edge (здесь "back edge" — обратное ребро, "tree edge" — ребро дерева) + +Тогда, из вершины $v$ или её потомка есть обратное ребро в её предка тогда и только тогда, когда найдётся такой сын $to$ , что $fup[to] \leq tin[v]$. (Если $fup[to] = tin[v]$, то это означает, что найдётся обратное ребро, приходящее точно в $v$; если же $fup[to] < tin[v]$, то это означает наличие обратного ребра в какого-либо предка вершины $v$.) + +Таким образом, если для текущего ребра $(v,to)$ (принадлежащего дереву поиска) выполняется $fup[to] > tin[v]$, то это ребро является мостом; в противном случае оно мостом не является. + +### 1.6. Компоненты вершинной двусвязности. Точки сочленения. Поиск точек сочленения + +#### Компоненты вершинной двусвязности + +**Компонентами вершинной двусвязности** графа, называют его подграфы, множества ребер которых — классы эквивалентности вершинной двусвязности, а множества вершин — множества всевозможных концов ребер из соответствующих классов. + +#### Точки сочленения + +**Точкой сочленения** называется вершина, при удалении которой связный неориентированный граф становится несвязным. + +#### Поиск точек сочленения + +Алгоритм: + +Запустим обход в глубину из произвольной вершины графа; обозначим её через $root$. Заметим следующий факт (который несложно доказать): + +Пусть мы находимся в обходе в глубину, просматривая сейчас все рёбра из вершины $v \neq root$. Тогда, если текущее ребро $(v, to)$ таково, что из вершины $to$ и из любого её потомка в дереве обхода в глубину нет обратного ребра в какого-либо предка вершины v, то вершина $v$ является точкой сочленения. В противном случае, т.е. если обход в глубину просмотрел все рёбра из вершины $v$, и не нашёл удовлетворяющего вышеописанным условиям ребра, то вершина $v$ не является точкой сочленения. (В самом деле, мы этим условием проверяем, нет ли другого пути из $v$ в $to$ ) + +Рассмотрим теперь оставшийся случай: $v = root$. Тогда эта вершина является точкой сочленения тогда и только тогда, когда эта вершина имеет более одного сына в дереве обхода в глубину. (В самом деле, это означает, что, пройдя из $root$ по произвольному ребру, мы не смогли обойти весь граф, откуда сразу следует, что $root$ — точка сочленения). + +Теперь осталось научиться проверять этот факт для каждой вершины эффективно. Для этого воспользуемся "временами входа в вершину", вычисляемыми алгоритмом поиска в глубину. + +Итак, пусть $tin[v]$ — это время захода поиска в глубину в вершину v. Теперь введём массив $fup[v]$, который и позволит нам отвечать на вышеописанные запросы. Время $fup[v]$ равно минимуму из времени захода в саму вершину $tin[v]$, времён захода в каждую вершину p, являющуюся концом некоторого обратного ребра $(v, p)$, а также из всех значений $fup[to]$ для каждой вершины $to$ , являющейся непосредственным сыном $v$ в дереве поиска: + +$fup[v] = \min(tin[v], tin[p], fup(to))$ $(v, p)$ - back edge, $(v, to)$ - tree edge (здесь "back edge" — обратное ребро, "tree edge" — ребро дерева) + +Тогда, из вершины $v$ или её потомка есть обратное ребро в её предка тогда и только тогда, когда найдётся такой сын $to$ , что $fup[to] < tin[v]$. + +Таким образом, если для текущего ребра $(v,to)$ (принадлежащего дереву поиска) выполняется $fup[to] \geq tin[v]$, то вершина $v$ является точкой сочленения. Для начальной вершины $v = root$ критерий другой: для этой вершины надо посчитать число непосредственных сыновей в дереве обхода в глубину. + +### 1.7. Волновой алгоритм. Обход в ширину (применение очереди в волновом алгоритме) + +#### Волновой алгоритм + +**Алгоритм волновой трассировки** (*волновой алгоритм*) — алгоритм поиска пути, алгоритм поиска кратчайшего пути на планарном графе. Принадлежит к алгоритмам, основанным на методах поиска в ширину. + +Алгоритм предназначен для поиска кратчайшего пути от стартовой ячейки к конечной ячейке, если это возможно, либо, при отсутствии пути, выдать сообщение о непроходимости. + +Работа алгоритма включает в себя три этапа: инициализацию, распространение волны и восстановление пути. + +Инициализация + +```c +Пометить стартовую ячейку +d := 0 +``` + +Распространение волны + +```c +ЦИКЛ + ДЛЯ каждой ячейки loc, помеченной числом d + пометить все соседние свободные непомеченные ячейки числом d + 1 + КЦ + d := d + 1 +ПОКА (финишная ячейка не помечена) И (есть возможность распространения волны) +``` + +Восстановление пути + +```c +ЕСЛИ финишная ячейка помечена +ТО + перейти в финишную ячейку + ЦИКЛ + выбрать среди соседних ячейку, помеченную числом на 1 меньше числа в текущей ячейке + перейти в выбранную ячейку и добавить её к пути + ПОКА текущая ячейка — не стартовая + ВОЗВРАТ путь найден +ИНАЧЕ + ВОЗВРАТ путь не найден +``` + +#### Обход в ширину (применение очереди в волновом алгоритме) + +**Поиск в ширину** (англ. *breadth-first search*) — один из основных алгоритмов на графах, позволяющий находить все кратчайшие пути от заданной вершины и решать многие другие задачи. + +Описание алгоритма + +На вход алгоритма подаётся невзвешенный граф и номер стартовой вершины $s$. Граф может быть как ориентированным, так и неориентированным — для алгоритма это не важно. + +Основную идею алгоритма можно понимать как процесс «поджигания» графа: на нулевом шаге мы поджигаем вершину $s$, а на каждом следующем шаге огонь с каждой уже горящей вершины перекидывается на всех её соседей, в конечном счете поджигая весь граф. + +Если моделировать этот процесс, то за каждую итерацию алгоритма будет происходить расширение «кольца огня» в ширину на единицу. Номер шага, на котором вершина $v$ начинает гореть, в точности равен длине её минимального пути из вершины s. + +```c++ +std::vector graph; + +void bfs(int s) { + std::queue queue; + queue.push(s); + + std::vector depth(n, -1), parent(n); + depth[s] = 0; + + while (!queue.empty()) { + int vertex = queue.front(); + queue.pop(); + for (int u : g[vertex]) { + if (depth[u] == -1) { + queue.push(u); + depth[u] = depth[vertex] + 1; + parent[u] = vertex; + } + } + } +} +``` + +Теперь, чтобы восстановить кратчайший путь до какой-то вершины v, это можно сделать через массив p: + +```c++ +while (v != s) { + std::cout << v << endl; + v = p[v]; +} +``` + +Обратим внимание, что путь выведется в обратном порядке. + +### 1.8. Критерий существования Эйлерова пути и цикла в ориентированном и неориентированном графе. Поиск эйлерова пути и цикла + +#### Критерий существования Эйлерова пути и цикла в неориентированном графе + +Согласно теореме, доказанной Эйлером, эйлеров цикл существует тогда и только тогда, когда + +1. Граф связный или будет являться связным, если удалить из него все изолированные вершины. +2. В нём отсутствуют вершины нечётной степени. + +Эйлеров путь в графе существует тогда и только тогда, когда + +1. Граф связный +2. Содержит не более двух вершин нечётной степени. Ввиду леммы о рукопожатиях, число вершин с нечётной степенью должно быть чётным. А значит эйлеров путь существует только тогда, когда это число равно нулю или двум. Причём, когда оно равно нулю, эйлеров путь вырождается в эйлеров цикл. + +#### Критерий существования Эйлерова пути и цикла в ориентированном графе + +В ориентированном графе $G = (V, E)$ существует эйлеров цикл тогда и только тогда, когда: + +1. Входная степень любой вершины равна ее выходной степени. +2. Все компоненты слабой связности кроме, может быть одной, не содержат ребер. + +В ориентированном графе $G = (V, E)$ существует эйлеров путь если: + +1. Входная степень любой вершины равна ее выходной степени, кроме двух вершин графа, для одной из которых $(deg+) − (deg−) = 1$, а для другой $(deg+) − (deg−) = −1$. +2. Все компоненты слабой связности кроме, может быть одной, не содержат ребер. + +#### Поиск эйлерова пути и цикла + +**Поиск эйлерова пути в графе:** + +Можно всегда свести задачу поиска эйлерова пути к задаче поиска эйлерова цикла. Действительно, предположим, что эйлерова цикла не существует, а эйлеров путь существует. Тогда в графе будет ровно 2 вершины нечётной степени. Соединим эти вершины ребром, и получим граф, в котором все вершины чётной степени, и эйлеров цикл в нём существует. Найдём в этом графе эйлеров цикл (алгоритмом, описанным ниже), а затем удалим из ответа несуществующее ребро. + +**Поиск эйлерова цикла в графе:** + +Будем рассматривать самый общий случай — случай ориентированного мультиграфа, возможно, с петлями. Также мы предполагаем, что эйлеров цикл в графе существует (и состоит хотя бы из одной вершины). Для поиска эйлерова цикла воспользуемся тем, что эйлеров цикл — это объединение всех простых циклов графа. Следовательно, наша задача — эффективно найти все циклы и эффективно объединить их в один. + +Реализовать это можно, например, так, рекурсивно: + +```c +procedure find_all_cycles (v) +var массив cycles +1. пока есть цикл, проходящий через v, находим его добавляем все вершины найденного цикла в массив cycles (сохраняя порядок обхода) удаляем цикл из графа +2. идем по элементам массива cycles каждый элемент cycles[i] добавляем к ответу из каждого элемента рекурсивно вызываем себя: find_all_cycles(cycles[i]) +``` + +Достаточно вызвать эту процедуру из любой вершины графа, и она найдёт все циклы в графе, удалит их из графа и объединит их в один эйлеров цикл. +Для поиска цикла на шаге 1 используем поиск в глубину. +Сложность полученного алгоритма — $O(|E|)$, то есть линейная относительно количества рёбер в данном графе + +## 2. Планарность графа + +### 2.1. Формула Эйлера. Теорема Портнягина-Куратовского. Теорема Вагнера + +#### Формула Эйлера + +Для произвольного плоского связного графа $G$ с $V$ вершинами, $E$ ребрами и $F$ гранями справедливо следующее соотношение: $V − E + F = 2$. + +#### Теорема Портнягина-Куратовского + +**Теорема Понтрягина — Куратовского**, или **теорема Куратовского**, — теорема в теории графов, дающая необходимое и достаточное условие планарности графа. Имеет эквивалентную формулировку на языке миноров, так называемою теорему Вагнера. + +Теорема утверждает, что графы $K_5$ (полный граф на 5 вершинах) и $K_{3,3}$ (полный двудольный граф имеющий по 3 вершины в каждой доле) являются единственными минимальными непланарными графами. + +#### Теорема Вагнера + +**Теорема Вагнера** — характеризация планарных графов тесно связанная с теоремой Понтрягина — Куратовского. + +Теорема утверждает, что конечный граф является планарным тогда и только тогда, когда его миноры не включают ни $K_5$ (полный граф с пятью вершинами), ни $K_{3,3}$ (коммунальный граф, полный двудольный граф с тремя вершинами в каждой доле). + +### 2.2. Гамма алгоритм. Контактная вершина + +#### Гамма алгоритм + +Чтобы проверить планарность графа и произвести его плоскую укладку, удобно пользоваться гамма-алгоритмом. +Определения: + +1. Сегмент графа $Г$ относительно подграфа $H$ — компонента связности графа $G \setminus H$. +2. **Допустимая грань сегмента** — грань графа $H$, содержащая все контактные вершины. +3. **Контактная вершина** сегмента $S$ графа $G$ подграфа $H$ — любая вершина в $S$ и в $H$. +4. **Гамма-цепь сегмента** — любая цепь без повторов вершин, содержащих ровно две контактные вершины — начало и конец. + +Алгоритм: + +1. Уложить на плоскость любой цикл $H$ графа $G$ без повторов вершин. +2. Построить все сегменты $S_1$ , $...$ , $S_k$ графа $G$ по $H$. +3. Если есть сегмент $S_i$ c одной допустимой гранью — выбрать его. +4. Если все сегменты имеют несколько дополнительных граней — выбрать любой. +5. Выбрать произвольную гамма-цепь сегмента и уложить её в допустимую грань. +6. Перейти к шагу (2), добавив гамма-цепь к графу $H$. + +### 2.3. Теорема о корректности Гамма алгоритма. Асимптотика алгоритма + +#### Теорема о корректности Гамма алгоритма + +**Теорема:** +Гамма-алгоритм корректен, то есть если $G$ — планарный граф, то результатом каждого шага гамма-алгоритма является частичная укладка $G'$. + +**Доказательство:** + +Докажем индукцией по числу шагов. + +База индукции: полученный на этапе инициализации граф $G_0$ является простым циклом, он будет присутствовать в любой укладке графа $G$. Таким образом, $G_0$ является частичной укладкой. + +Шаг индукции: пусть граф $G_{k−1}$, полученный на $k−1$-м шаге работы алгоритма, является частичной укладкой. Докажем, что граф $G_k = G_{k−1} \cup L_k$, полученный на $k$-м шаге присоединением цепи $L_k$, также является частичной укладкой. + +Заметим, что на текущем шаге нет такого сегмента $S$ относительно $G_{k−1}$, для которого бы выполнялось равенство $Γ(S) = \varnothing$, так как в противном случае существовала бы цепь этого сегмента, контактные вершины которой принадлежали бы разным граням и укладка которой была бы невозможна. Следовательно, нельзя было бы уложить $S$,что противоречит тому, что $G$ — планарный граф. Значит, мы можем рассматривать только следующие два случая: + +1. Существует сегмент $S$ для которого есть единственная вмещающая грань $Γ$, то есть $|Γ(S)| = 1$. Так как только грани $Γ$ принадлежат все контактные вершины $S$, то укладка этого сегмента в эту грань неизбежна. Это значит, что помещая любую цепь $L \subset S$, снова получим частичную укладку графа. +2. Для любого сегмента $S$ $|Γ(S)| \geq 2$. Построим граф $A(G'k−1)$, который по лемме 2 является двудольным. Рассмотрим его связную компоненту $K$, которая содержит не менее двух вершин. Граф $K$ также является двудольным. По лемме 1 для любого сегмента $S \in K$ справедливо $Γ(S)={Γ1, Γ2}$. Так как граф $K$ двудольный, то мы можем по очереди помещать сегменты $K$ в разные грани, причем конфликтующих сегментов не возникнет в силу четности всех циклов в графе. Результатом будет частичная укладка графа. + +Таким образом, на каждом шаге мы получаем частичную укладку графа, что доказывает корректность гамма-алгоритма. + +**Асимптотика алгоритма - $O(n^3)$.** + +## 3. Кратчайшие пути во взвешенном графе + +### 3.1. Алгоритм Дейкстры. Доказательство корректности(с доказательством). Оценка времени работы. Дерево кратчайших путей + +#### Алгоритм Дейкстры + +**Алгоритм Дейкстры** (англ. *Dijkstra’s algorithm*) — алгоритм на графах, изобретённый нидерландским учёным Эдсгером Дейкстрой в 1959 году. Находит кратчайшие пути от одной из вершин графа до всех остальных. Алгоритм работает только для графов без рёбер отрицательного веса. + +```c +func dijkstra(s): + for v in V + d[v] = infinity + used[v] = false + d[s] = 0 + for i in V + v = null + for j in V // найдём вершину с минимальным расстоянием + if !used[j] and (v == null or d[j] < d[v]) + v = j + if d[v] == infinity + break + used[v] = true + for e : исходящие из v рёбра // произведём релаксацию по всем рёбрам, исходящим из v + if d[v] + e.len < d[e.to] + d[e.to] = d[v] + e.len +``` + +#### Доказательство корректности + +Пусть $G=(V,E)$ — ориентированный взвешенный граф, вес рёбер которого неотрицателен, s — стартовая вершина. Тогда после выполнения алгоритма Дейкстры $d(u)=\rho(s, u)$ для всех $u$, где $\rho (s,u)$ — длина кратчайшего пути из вершины $s$ в вершину $u$. + +Докажем по индукции, что в момент посещения любой вершины $u$, $d(u)= \rho (s,u)$. + +На первом шаге выбирается $s$, для неё выполнено: $d(s) = \rho (s,s) = 0$. Пусть для $n$ первых шагов алгоритм сработал верно и на $n+1$ шагу выбрана вершина $u$. Докажем, что в этот момент $d(u)= \rho (s,u)$. Для начала отметим, что для любой вершины $v$, всегда выполняется $d(v) \geq \rho (s,v)$ (алгоритм не может найти путь короче, чем кратчайший из всех существующих). Пусть $P$ — кратчайший путь из $s$ в $u$, $v$ — первая непосещённая вершина на $P$, $z$ — предшествующая ей (следовательно, посещённая). Поскольку путь $P$ кратчайший, его часть, ведущая из $s$ через $z$ в $v$, тоже кратчайшая, следовательно $\rho(s,v)= \rho(s,z)+w(zv)$. По предположению индукции, в момент посещения вершины $z$ выполнялось $d(z)= \rho (s,z)$, следовательно, вершина $v$ тогда получила метку не больше чем $d(z) + w(zv) = \rho (s,z) + w(zv) = \rho(s,v)$, следовательно, $d(v)=\rho(s,v)$. С другой стороны, поскольку сейчас мы выбрали вершину $u$, её метка минимальна среди непосещённых, то есть $d(u) \leq d(v) = \rho (s,v) \leq \rho (s,u)$, где второе неравенсто верно из-за ранее упомянутого определения вершины $v$ в качестве первой непосещённой вершины на $P$, то есть вес пути до промежуточной вершины не превосходит веса пути до конечной вершины вследствие неотрицательности весовой функции. Комбинируя это с $d(u) \geq \rho(s,u)$, имеем $d(u)=\rho(s,u)$, что и требовалось доказать. Поскольку алгоритм заканчивает работу, когда все вершины посещены, в этот момент $d(u)= \rho (s,u)$ для всех $u$. + +#### Оценка времени работы + +Если $m = n ^ 2$(граф плотный), то $O(n^2)$. +Если $m = n$(граф разреженный), то $O(m log n)$. + +#### Дерево кратчайших путей + +Решение ряда прикладных задач сводится к нахождению дерева кратчайших путей с корнем в заданной вершине $s$. Такое дерево дает кратчайшие $(s, v)$-пути от заданной вершины $s$ до любой вершины $v$ орграфа $G = (V, Е)$. Если веса всех дуг орграфа $G$ неотрицательны, то дерево кратчайших путей можно построить с помощью алгоритма Дейкстры. + +### 3.2. Потенциалы. Условие применимости алгоритма Дейкстры для измененных длин ребер. Потенциал $\pi(v) = \rho(v, t)$ + +#### Потенциалы + +Пусть дана транспортная сеть $G(V,E)$, где $V$ — множество вершин графа, а $E$ — множество рёбер. Введем в каждой вершине потенциал $\pi(v)$. Тогда потенциальный вес (то есть стоимость) ребра $(u,v)$ определяется как $w_p(u,v)=w(u,v)+\pi(u)−\pi(v)$ + +#### Условие применимости алгоритма Дейкстры для измененных длин ребер + +Можно применять, когда нет отрицательных циклов (следует из курса дискретной математики 1 курс 2 семестр) + +#### Потенциал $\pi(v) = \rho(v, t)$ + +### 3.3. Алгоритм A*. Условие монотонности на эвристику. Примеры эвристик + +#### Алгоритм A* + +**Алгоритм A*** - алгоритм поиска по первому наилучшему совпадению на графе, который находит маршрут с наименьшей стоимостью от одной вершины (начальной) к другой (целевой, конечной). + +Порядок обхода вершин определяется эвристической функцией «расстояние + стоимость» (обычно обозначаемой как $f(x)$). Эта функция — сумма двух других: функции стоимости достижения рассматриваемой вершины (x) из начальной (обычно обозначается как $g(x)$ и может быть как эвристической, так и нет), и функции эвристической оценки расстояния от рассматриваемой вершины к конечной (обозначается как $h(x)$). + +1. Функция $h(x)$ должна быть *допустимой эвристической оценкой*, то есть не должна переоценивать расстояния к целевой вершине. Например, для задачи маршрутизации $h(x)$ может представлять собой расстояние до цели по прямой линии, так как это физически наименьшее возможное расстояние между двумя точками. +2. Более сильное условие — функция $h(v)$ должна быть **монотонной**. (Эвристическая функция $h(v)$ называетя **монотонной** (или преемственной), если для любой вершины $v_1$ и ее потомка $v_2$ разность $h(v_1)$ и $h(v_2)$ не превышает фактического веса ребра $c(v_1, v_2)$ от $v_1$ до $v_2$, а эвристическая оценка целевого состояния равна нулю.) + +```c +function A*(start, goal, f) + % множество уже пройденных вершин + var closed := the empty set + % множество частных решений + var open := make_queue(f) + enqueue(open, path(start)) + while open is not empty + var p := remove_first(open) + var x := the last node of p + if x in closed + continue + if x = goal + return $\rho$ + add(closed, x) + % добавляем смежные вершины + foreach y in successors(x) + enqueue(open, add_to_path(p, y)) + return failure +``` + +#### Примеры эвристик + +- Если мы можем перемещаться в четырех направлениях, то в качестве эвристики стоит выбрать манхэттенское расстояние $h(v)=|v.x−goal.x|+|v.y−goal.y|$. +- Расстояние Чебышева применяется, когда к четырем направлениям добавляются диагонали: $h(v)=max(|v.x−goal.x|,|v.y−goal.y|)$. +- Если передвижение не ограничено сеткой, то можно использовать евклидово расстояние по прямой: $h(v)=((v.x−goal.x) ^ 2+(v.y−goal.y)^2) ^ {1/2}$. + +### 3.4. Алгоритм Форда-Беллмана. Хранение в матрице: $D_{vk}$ равно длине кратчайшего пути до вершины $v$ за ровно $k$ ребер (не более $k$ ребер). Доказательство корректности (полное). Оценка времени работы + +Количество путей длины $k$ рёбер можно найти с помощью метода динамического программирования. Пусть $d[k][u]$ — количество путей длины $k$ рёбер, заканчивающихся в вершине $u$. Тогда $d[k][u]= \Sigma v: v, u \in E(d[k−1][v])$. + +Аналогично посчитаем пути кратчайшей длины. Пусть $s$ — стартовая вершина. Тогда $d[k][u]=\min v: v, u \in E(d[k−1][v]+w(u,v))$, при этом $d[0][s]=0$, а $d[0][u]=+\infty$ + +Если существует кратчайший путь от s до t, то $\rho(s,t)=\min d[k][t](k=0..n−1)$ + +Пусть кратчайший путь состоит из $k$ ребер, тогда корректность формулы следует из динамики, приведенной ниже. + +```c++ +for k = 0 to |V|−2 // вершины нумеруются с единицы + for v∈V + for (u,v)∈E + d[k + 1][v] = min(d[k + 1][v], d[k][u] + ω(u,v)) // ω(u,v) — вес ребра uv +``` + +```c++ +bool fordBellman(s): + for v∈V + d[v] = 1 + d[s] = 0 + for i = 0 to |V|−1 + for (u,v)∈E + if d[v] > d[u] + ω(u,v) // ω(u,v) — вес ребра uv + d[v] = d[u] + ω(u,v) + for (u,v)∈E + if d[v] > d[u] + ω(u,v) + return false + return true +``` + +#### Доказательство корректности(полное) + +Во-первых, сразу заметим, что для недостижимых из $v$ вершин алгоритм отработает корректно: для них метка $d[]$ так и останется равной бесконечности (т.к. алгоритм Форда-Беллмана найдёт какие-то пути до всех достижимых из $s$ вершин, а релаксация во всех остальных вершинах не произойдёт ни разу). + +Докажем теперь следующее утверждение: после выполнения $i$ фаз алгоритм Форда-Беллмана корректно находит все кратчайшие пути, длина которых (по числу рёбер) не превосходит $i$. + +Иными словами, для любой вершины $a$ обозначим через $k$ число рёбер в кратчайшем пути до неё (если таких путей несколько, можно взять любой). Тогда это утверждение говорит о том, что после $k$ фаз этот кратчайший путь будет найден гарантированно. + +**Доказательство.** + +Рассмотрим произвольную вершину a, до которой существует путь из стартовой вершины v, и рассмотрим кратчайший путь до неё: $(p_0=v, p_1, ..., p_k=a)$. Перед первой фазой кратчайший путь до вершины $p_0=v$ найден корректно. Во время первой фазы ребро $(p_0,p_1)$ было просмотрено алгоритмом Форда-Беллмана, следовательно, расстояние до вершины $p_1$ было корректно посчитано после первой фазы. Повторяя эти утверждения k раз, получаем, что после $k$-й фазы расстояние до вершины $p_k=a$ посчитано корректно, что и требовалось доказать. + +Последнее, что надо заметить — это то, что любой кратчайший путь не может иметь более $n-1$ ребра. Следовательно, алгоритму достаточно произвести только $n-1$ фазу. После этого ни одна релаксация гарантированно не может завершиться улучшением расстояния до какой-то вершины. + +#### Оценка времени работы + +Инициализация занимает $\theta(V)$ времени, каждый из $|V|−1$ проходов требует $\theta(E)$ времени, обход по всем ребрам для проверки наличия отрицательного цикла занимает $O(E)$ времени. Значит алгоритм Беллмана-Форда работает за $O(VE)$ времени. + +### 3.5. Восстановление пути. Детектирование цикла отрицательного веса. Поиск самого цикла + +#### Восстановление пути + +```c++ +void solve() { + vector d (n, INF); + d[v] = 0; + vector p (n, -1); + for (;;) { + bool any = false; + for (int j=0; j d[e[j].a] + e[j].cost) { + d[e[j].b] = d[e[j].a] + e[j].cost; + p[e[j].b] = e[j].a; + any = true; + } + if (!any) break; + } + + if (d[t] == INF) + cout << "No path from " << v << " to " << t << "."; + else { + vector path; + for (int cur=t; cur!=-1; cur=p[cur]) + path.push_back (cur); + reverse (path.begin(), path.end()); + + cout << "Path from " << v << " to " << t << ": "; + for (size_t i=0; iKey << "," << t->Id->Key << ") "; + Ostov_Depth ((*t).Id); + } + t = (*t).Next; + } +} +``` + +### 4.2. Определение минимального остовного дерева + +**Минимальное остовное дерево** (англ. *minimum spanning tree*) графа — это его ациклический связный подграф, в который входят все его вершины, обладающий минимальным суммарным весом ребер. Заметим, что граф может содержать несколько минимальных остовных деревьев. + +### 4.3. Теорема о разрезе. Доказательство + +#### Теорема о разрезе + +**Теорема Форда — Фалкерсо́на** — теорема о максимальном потоке в графе, тесно связанная с теоремой Менгера. +Звучит так: величина максимального потока в графе путей равна величине пропускной способности его минимального разреза. + +*Доказательство:* + +**Достаточность**: любой поток между вершинами $t$ и $s$ меньше или равен величине любого сечения. Пусть дан некоторый поток и некоторое сечение. Величина данного потока складывается из величин «грузов», перевозимых по всем возможным путям из вершины $t$ в $s$. Каждый такой путь обязан иметь общее ребро с данным сечением. Так как по каждому ребру сечения суммарно нельзя перевести «груза» больше, чем его пропускная способность, поэтому сумма всех грузов меньше или равна сумме всех пропускных способностей рёбер данного сечения. Утверждение доказано. + +Отсюда следует, что любой поток меньше или равен величине минимального сечения, а значит и максимальный поток меньше или равен величине минимального сечения. + +### 4.4. Алгоритм Прима. Аналогия с алгоритмом Дейкстры. Оценка времени работы для различных реализаций очереди с приоритетом: бинарная куча, Фибоначчиева куча (последнее без доказательства) + +#### Алгоритм Прима + +**Алгоритм Прима** (англ. *Prim's algorithm*) — алгоритм поиска минимального остовного дерева (англ. minimum spanning tree, MST) во взвешенном неориентированном связном графе. + +#### Аналогия с алгоритмом Дейкстры + +Данный алгоритм очень похож на алгоритм Дейкстры. Будем последовательно строить поддерево $F$ ответа графе $G$, поддерживая приоритетную очередь $Q$ из вершин $G \setminus F$, в которой ключом для вершины $v$ является $\min(\omega(uv) (u \in V(F), (u, v) \in E(G)))$ — вес минимального ребра из вершин $F$ в вершины $G \setminus F$. Также для каждой вершины в очереди будем хранить $p(v)$ — вершину $u$, на которой достигается минимум в определении ключа. Дерево $F$ поддерживается неявно, и его ребра — это пары $(v,p(v))$, где $v \in G \setminus {r} \setminus Q$, а $r$ — корень $F$. Изначально $F$ пусто и значения ключей у всех вершин равны $+\infty$. Выберём произвольную вершину $r$ и присвоим её ключу значение 0. На каждом шаге будем извлекать минимальную вершину $v$ из приоритетной очереди и релаксировать все ребра $(v, u)$, такие что $u \in Q$, выполняя при этом операцию $\rm decreaseKey$ над очередью и обновление $p(v)$. Ребро $(v,p(v))$ при этом добавляется к ответу. + +#### Оценка времени работы для различных реализаций очереди с приоритетом: бинарная куча, Фибоначчиева куча + +Двоичная куча - $O(ElogV)$ + +(Чтобы упростить операцию $\rm decreaseKey$ можно написать кучу на основе сбалансированного бинарного дерева поиска. Тогда просто удалим вершину и добавим ее обратно уже с новым ключом. Асимптотика таких преобразований $O(logn)$. Если же делать с бинарной кучей, то вместо операции $\rm decreaseKey$, будем всегда просто добавлять вершину с новым ключом, если из кучи достали вершину с ключом, значение которого больше чем у нее уже стоит, просто игнорировать. Вершин в куче будет не больше $2 \cdot n$, следовательно, операция $\rm extractMin$ будет выполняться за $O(log(\cdot n))$, что равно $O(logn)$. Максимальное количество вершин, которое мы сможем достать, равняется количеству ребер, то есть m, поэтому общая асимптотика составит $O(mlogn)$, что хорошо только на разреженных графах.) + +Фибоначчиева куча - $O(VlogV+E)$ + +### 4.5. Алгоритм Прима. Доказательство корректности с помощью теоремы о разрезе + +Приведем еще раз теорему о разрезе: +Для любого разбиения множества вершин $V = A \cup B$, существует минимальный остов, содержащий $e$ – минимальное по весу ребро, проходящее через разрез $\langle A, B \rangle$ + +Доказательство. Возьмём минимальный остов без $e$. Добавим $e$, получится цикл. Два ребра этого цикла проходят через разрез $\langle A, B \rangle$. Старое неменьше $e$, удалим его. Если же в процессе построения $\rm min$ остова уже известно подмножество рёбер будущего $\rm min$ остова, эти рёбра задают компоненты связности. Мы можем сжать компоненты в вершины и для конденсации применить лемму о разрезе. + +Следствие $X$ – подмножество рёбер некого минимального остова. Зафиксируем разрез $V = A \cup B$ такой, что все рёбра $X$ не пересекают разрез. Тогда $\exists$ минимальный остов, содержащий $X \cup \{e\}$, где $e$ – минимальное по весу ребро, проходящее через разрез $\langle A, B \rangle$. + +#### Доказательство корректности с помощью теоремы о разрезе + +Разрез – текущее множество $A$ и дополнение $V \setminus A$. + +### 4.6. Алгоритм Краскала. Доказательство корректности. Оценка времени работы + +#### Алгоритм Краскала + +Будем последовательно строить подграф $F$ графа $G$ ("растущий лес"), пытаясь на каждом шаге достроить $F$ до некоторого $MST$. Начнем с того, что включим в $F$ все вершины графа $G$. Теперь будем обходить множество $E(G)$ в порядке неубывания весов ребер. Если очередное ребро $e$ соединяет вершины одной компоненты связности $F$, то добавление его в остов приведет к возникновению цикла в этой компоненте связности. В таком случае, очевидно, $e$ не может быть включено в $F$. Иначе $e$ соединяет разные компоненты связности $F$, тогда существует $\langle S, T \rangle$ разрез такой, что одна из компонент связности составляет одну его часть, а оставшаяся часть графа — вторую. Тогда $e$ — минимальное ребро, пересекающее этот разрез. Значит, из леммы о безопасном ребре следует, что $e$ является безопасным, поэтому добавим это ребро в $F$. На последнем шаге ребро соединит две оставшиеся компоненты связности, полученный подграф будет минимальным остовным деревом графа $G$. Для проверки возможности добавления ребра используется система непересекающихся множеств. + +**Доказательство корректности:** + +Обратимся к теореме о разрезе из Пункта 4.5 +Добавляем ребро $(a, b)$. Разрез – любой такой, что $(a, b)$ через него проходит. + +**Оценка времени работы** - $O(ElogE)$. + +### 4.7. Система непересекающихся множеств. Эвристика потенциалов без доказательства. Эвристика сжатия пути без доказательства. Почти константное время работы + +### 4.8. Алгоритм Борувки. Доказательство. Оценка времени работы + +#### Алгоритм Борувки + +Алгоритм состоит из нескольких шагов: + +1. Изначально каждая вершина графа $G$ — тривиальное дерево, а ребра не принадлежат никакому дереву. +2. Для каждого дерева $T$ найдем минимальное инцидентное ему ребро. Добавим все такие ребра. +3. Повторяем шаг 2 пока в графе не останется только одно дерево $T$. + +Данный алгоритм может работать неправильно, если в графе есть ребра равные по весу. Например, полный граф из трех вершин, вес каждого ребра равен один. В $T$ могут быть добавлены все три ребра. Избежать эту проблему можно, например, выбирая в первом пункте среди ребер, равных по весу, ребро с наименьшим номером. + +**Доказательство:** + +Обратимся к теореме о разрезе из Пункта 4.5 +Рёбра, которые хочет добавить Борувка, разобьём на компоненты связности. Каждая компонента – дерево, рёбра компоненты будем добавлять по одному от корня к листьям. Добавляя +очередное ребро ведущее в лист $v$, используем разрез $\langle \{v\}, V \setminus \{v\} \rangle$ и лемму о разрезе. + +##### Оценка времени работы + +На $i$-ой итерации внешнего цикла каждая компонента состоит как минимум из двух компонент из $(i−1)$-й итерации. Значит, на каждой итерации число компонент уменьшается как минимум в 2 раза. Тогда внешний цикл повторяется $O(logV)$ раз, так как количество компонент изначально равно количеству вершин. Что же касается внутреннего цикла, то он выполняется за $O(E)$, где $E$ — количество рёбер в исходном графе. Следовательно конечное время работы алгоритма $O(ElogV)$. + +### 4.9. Приближение решения задачи коммивояжера с помощью минимального остовного дерева + +**Алгоритм Кристофидеса** или **алгоритм Кристофидеса-Сердюкова** — это алгоритм поиска приближённых решений задачи коммивояжёра для случаев, когда расстояния образуют метрическое пространство (симметричны и удовлетворяют неравенству треугольника). + +Алгоритм является аgпроксимационным алгоритмом, который гарантирует, что решения находятся в пределах 3/2 от длины оптимального решения. Алгоритм назван именем Никоса Кристофидеса и Анатолия Ивановича Сердюкова, которые независимо друг от друга нашли его в 1976, и он обладает лучшим аппроксимационным коэффициентом, который был доказан для задачи коммивояжёра на метрических пространствах общего вида, хотя известны лучшие приближения для некоторых специальных случаев. + +Пусть $G=(V,w)$ будет представителем задачи коммивояжёра. То есть $G$ является полным графом на множестве вершин $V$, а функция $w$ назначает неотрицательные вещественные веса каждому ребру графа $G$. +Алгоритм можно описать на псевдокоде следующим образом: + +1. Создаём минимальное остовное дерев $T$ графа $G$. +2. Пусть $O$ будет набором вершин с нечётными степенями в $T$. Согласно лемме о рукопожатиях, $O$ имеет чётное число вершин. +3. Находим совершенное паросочетание $M$ минимального веса в порождённом подграфе, заданным вершинами из $O$. +4. Комбинируем рёбра $M$ и $T$ с образованием связного мультиграфа $H$, в котором каждая вершина имеет чётную степень. +5. Образуем эйлеров цикл в $H$. +6. Преобразуем цикл, найденный на предыдущем шаге, в гамильтонов цикл путём пропуска повторяющихся вершин (сокращение). + +## 5. Потоки в сетях + +### 5.1. Определение сети. Определение потока. Физический смысл. Аналогия с законами Кирхгофа. Определение разреза. Понятия потока через разрез + +#### Определение сети + +**Сеть** (англ. *flow network*) представляет собой ориентированный граф, в котором каждое ребро имеет положительную пропускную способность (англ. *capacity*). + +#### Определение потока + +**Потоком** (англ. *flow*) $f$ в $G$ является действительная функция $f: V \times V \rightarrow R$, удовлетворяющая условиям: + +1) $f(u,v) = −f(v,u)$ (антисимметричность) +2) $f(u,v) \leq c(u,v)$ (ограничение пропускной способности), если ребра нет, то $f(u,v)=0$; +3) $\Sigma v \cdot f(u,v) = 0$ для всех вершин $u$, кроме $s$ и $t$ (закон сохранения потока). + +**Величина потока** $f$ определяется как $|f|=\Sigma v \in V \cdot f(s,v)$. + +#### Физический смысл. Аналогия с законами Кирхгофа + +#### Определение разреза + +$(s,t)$ - **разрезом** (англ. *s-t cut*) $\langle S,T \rangle$ в сети $G$ называется пара множеств $S$, $T$, удовлетворяющих условиям: $s \in S$,$t \in T$ и $S = V \setminus T$. + +#### Понятия потока через разрез + +**Поток в разрезе** (англ. *flow in the cut*) $\langle S,T \rangle$ обозначается $f(S,T)$ и вычисляется по формуле: $f(S,T)= \Sigma u \in S \cdot \Sigma v \in T \cdot f(u,v)$. + +### 5.2. Доказательство факта, что поток через любой разрез одинаковый + +$f(S,T) = f(S,V) − f(S,S) = f(S,V) = f(S \setminus s,V) + f(s,V) = f(s,V) = |f|$ + +1-е равенство выполняется, так как суммы не пересекаются: $f(S,V)=f(S,S)+f(S,T)$ +2-е равенство выполняется из-за антисимметричности: $f(S,S)=−f(S,S)=0$ +3-е равенство выполняется, как и 1-е, из-за непересекающихся сумм +4-е равенство выполняется из-за сохранения потока + +### 5.3. Понятие остаточной сети. Понятие дополняющего пути. Необходимость отсутствия дополняющего пути для максимальности потока + +#### Понятие остаточной сети + +Если задана некоторая транспортная сеть $G$ и поток $f$, то **остаточная сеть** $G_f$ - это сеть, состоящая из ребер с пропускными способностями, указывающими, как могут меняться потоки через ребра $G$. + +#### Понятие дополняющего пути + +Для заданной транспортной сети и потока **дополняющим путем** (англ. *augmenting path*) является простой путь из истока в сток в остаточной сети. + +#### Необходимость отсутствия дополняющего пути для максимальности потока + +В случае существования дополняющего пути поток можно увеличить. + +### 5.4. Теорема Форда-Фалкерсона + +**Теорема Форда — Фалкерсо́на** — теорема о максимальном потоке в графе, тесно связанная с теоремой Менгера. Звучит так: величина максимального потока в графе путей равна величине пропускной способности его минимального разреза. + +### 5.5. Алгоритм Форда-Фалкерсона. Поиск минимального разреза. Пример целочисленной сети, в котором алгоритм работает долго + +#### Алгоритм Форда-Фалкерсона + +Идея алгоритма заключается в следующем. Изначально величине потока присваивается значение 0: $f(u,v)=0$ для всех $u, v \in V$. Затем величина потока итеративно увеличивается посредством поиска увеличивающего пути (путь от источника $s$ к стоку $t$, вдоль которого можно послать ненулевой поток). В данной статье рассматривается алгоритм, осуществляющий этот поиск с помощью обхода в глубину (dfs). Процесс повторяется, пока можно найти увеличивающий путь. + +**Неформальное описание:** + +1. Обнуляем все потоки. Остаточная сеть изначально совпадает с исходной сетью. +2. В остаточной сети находим любой путь из источника в сток. Если такого пути нет, останавливаемся. +3. Пускаем через найденный путь (он называется увеличивающим путём или увеличивающей цепью) максимально возможный поток: + 1. На найденном пути в остаточной сети ищем ребро с минимальной пропускной способностью $c_{min}$. + 2. Для каждого ребра на найденном пути увеличиваем поток на $c_{min}$, а в противоположном ему — уменьшаем на $c_{min}$. + 3. Модифицируем остаточную сеть. Для всех рёбер на найденном пути, а также для противоположных (антипараллельных) им рёбер, вычисляем новую пропускную способность. Если она стала ненулевой, добавляем ребро к остаточной сети, а если обнулилась, стираем его. +4. Возвращаемся на шаг 2. + +#### Поиск минимального разреза + +Алгоритм Форда-Фалкерсона использует поиск в ширину для нахождения увеличивающего пути в сети и постепенно увеличивает поток через этот путь, пока не будет достигнут максимальный поток. Затем алгоритм ищет минимальный разрез, основываясь на значении потока. + +### 5.6. Алгоритм Эдмондса-Карпа. Доказательство, что кратчайшее расстояние в остаточной сети не уменьшается + +#### Алгоритм Эдмондса-Карпа + +**Алгоритм Эдмондса-Карпа** является реализацией метода Форда-Фалкерсона, в которой в качестве дополняющего пути выбирается кратчайший по рёбрам путь в остаточной сети (длины всех рёбер равны 1). + +**Описание:** + +1. Положим все потоки равными нулю. Остаточная сеть изначально совпадает с исходной сетью. +2. В остаточной сети находим кратчайший путь из источника в сток. Если такого пути нет, останавливаемся. +3. Пускаем через найденный путь (он называется увеличивающим путём или увеличивающей цепью) максимально возможный поток: + 1. На найденном пути в остаточной сети ищем ребро с минимальной пропускной способностью $c_{min}$. + 2. Для каждого ребра на найденном пути увеличиваем поток на $c_{min}$, а в противоположном ему — уменьшаем на $c_{min}$. + 3. Модифицируем остаточную сеть. Для всех рёбер на найденном пути, а также для противоположных им рёбер, вычисляем новую пропускную способность. Если она стала ненулевой, добавляем ребро к остаточной сети, а если обнулилась, стираем его. +4. Возвращаемся на шаг 2. + +**Доказательство, что кратчайшее расстояние в остаточной сети не уменьшаетcя:** + +**Лемма:** + +Кратчайшее расстояние от истока до каждой вершины не уменьшается с выполнением каждой итерации, т.е. $\rm level_{i+1}[v] >= level_i[v]$, где нижний индекс обозначает номер фазы, перед которой взяты значения этих переменных. + +**Доказательство:** + +Зафиксируем произвольную фазу $i$ и произвольную вершину $v$ и рассмотрим любой кратчайший $s - v$ путь $P$ в сети $G^R_{i+1}$ (напомним, так мы обозначаем остаточную сеть, взятую перед выполнением $i+1$-ой фазы). Очевидно, длина пути $P$ равна $\rm level_{i+1}[v]$. + +Заметим, что в остаточную сеть $G^R_{i+1}$ могут входить только рёбра из $G^R$, а также рёбра, обратные рёбрам из $G^R$ (это следует из определения остаточной сети). Рассмотрим два случая: + +Путь $P$ содержит только рёбра из $G^R$. Тогда, понятно, длина пути $P$ больше либо равна $level_i[v]$ (потому что $level_i[v]$ по определению — длина кратчайшего пути), что и означает выполнение неравенства. Путь $P$ содержит как минимум одно ребро, не содержащееся в $G^R$ (но обратное какому-то ребру из $G^R$). Рассмотрим первое такое ребро; пусть это будет ребро $(u,w)$. $s \rightarrow u \rightarrow w$ + +Мы можем применить нашу лемму к вершине $u$, потому что она подпадает под первый случай; итак, мы получаем неравенство $\rm level_{i+1}[u] >= level_i[u]$. + +Теперь заметим, что поскольку ребро $(u, w)$ появилось в остаточной сети только после выполнения $i$-ой фазы, то отсюда следует, что вдоль ребра $(w, u)$ был дополнительно пропущен какой-то поток; следовательно, ребро $(w, u)$ принадлежало слоистой сети перед $i$-ой фазой, а потому $\rm level_i[u] = level_i[w] + 1$. Учтём, что по свойству кратчайших путей $\rm level_{i+1}[w] = level_{i+1}[u] + 1$, и объединяя это равенство с двумя предыдущими неравенствами, получаем: +$\rm level_{i+1}[w] \geq level_i[w] + 2$. + +Теперь мы можем применять те же самые рассуждения ко всему оставшемуся пути до $v$ (т.е. что каждое инвертированное ребро добавляет к $\rm level$ как минимум два), и в итоге получим требуемое неравенство. + +### 5.7. Общая оценка времени работы алгоритма Эдмондса-Карпа + +Сложность алгоритма Эдмондса-Карпа равна $O(VE^2)$. + +### 5.8. Слоистая сеть. Алгоритм Диница. Оценка времени работы без доказательства + +#### Слоистая сеть + +**Слоистая сеть** — это сеть состоящая только из таких рёбер $(v, u)$ сети $G$, что $d(v) + 1 = d(u)$, где $d$ - расстояние от истока. + +#### Алгоритм Диница + +**Схема алгоритма:** + +1. Построить слоистую сеть по остаточной, если сток не достижим то завершить алгоритм +2. Пустить блокирующий поток в слоистой сети +3. Если алгоритм завершился, то в остаточной сети сток не достижим из истока, а значит не существует увеличивающего пути, следовательно найденный поток максимален. + +#### Оценка времени работы без доказательства + +Так как расстояние строго увеличивается количество итераций алгоритма = $O(V)$. В зависимости от выбора алгоритма поиска блокирующего потока время данного алгоритма может составить $O(VE^2), O(V^2E)$ или $O(V^3)$. + +## 6. RMQ. Sparse-table, дерево отрезков. LCA. Декартово дерево по неявному ключу + +### 6.1. RSQ и RMQ. Sparse-table + +#### RSQ и RMQ + +Дан числовой массив: $A[0]$, $A[1]$, ..., $A[N−1]$.\$ + +Нужно отвечать на запросы: + +- для данных $L$ и $R$ вычислить сумму $A[L] + ... + A[R−1]$; +- (опционально) обновить элемент: $A[i] := x$. + +Операция — не обязательно сумма, а элементы — не обязательно числа. + +- Сумма $\rightarrow$ RSQ (range sum query) +- Минимум $\rightarrow$ RMQ (range minimum query) + +### 6.2. Дерево отрезков. Обработка запросов от листьев. Обработка запросов от корня + +#### Дерево отрезков + +Дерево отрезков — это структура данных, которая позволяет эффективно (т.е. за асимптотику $O(log n)$) реализовать операции следующего вида: нахождение суммы/минимума элементов массива в заданном отрезке ($a[l ... r]$, где $l$ и $r$ поступают на вход алгоритма), при этом дополнительно возможно изменение элементов массива: как изменение значения одного элемента, так и изменение элементов на целом подотрезке массива (т.е. разрешается присвоить всем элементам $a[l ... r]$ какое-либо значение, либо прибавить ко всем элементам массива какое-либо число). + +Вообще, дерево отрезков — очень гибкая структура, и число задач, решаемых ей, теоретически неограниченно. Помимо приведённых выше видов операций с деревьями отрезков, также возможны и гораздо более сложные операции (см. раздел "Усложнённые версии дерева отрезков"). В частности, дерево отрезков легко обобщается на большие размерности: например, для решения задачи о поиске суммы/минимума в некотором подпрямоугольнике данной матрицы (правда, уже только за время $O(log^2 n)$). + +Важной особенностью деревьев отрезков является то, что они потребляют линейный объём памяти: стандартному дереву отрезков требуется порядка $4n$ элементов памяти для работы над массивом размера $n$. + +#### Обработка запросов от листьев. Обработка запросов от корня + +В дереве отрезков обработка запросов может осуществляться как от листьев (bottom-up), так и от корня (top-down). Это связано с тем, как информация распространяется вдоль дерева при выполнении запросов. + +1. **Обработка запросов от листьев (Bottom-Up):** + - Идея заключается в том, что сначала вычисляются значения для листьев (первоначальные элементы массива), а затем эта информация постепенно передается вверх по дереву для расчета значений на более высоких уровнях. + - Когда поступает запрос на определенный отрезок, начинаются вычисления от соответствующих листьев. Затем результаты объединяются на более высоких уровнях дерева, пока не достигнется корень. + - Этот подход обеспечивает эффективность расчетов для запросов на отрезках. +2. **Обработка запросов от корня (Top-Down):** + - Здесь запрос начинается с корня дерева, и информация передается вниз по дереву до тех пор, пока не достигнут нужные листья (или пока не выполнены условия запроса). + - Этот метод может быть полезен в случаях, когда запросы связаны с каким-то условием, которое позволяет пропускать некоторые поддеревья, не вычисляя их полностью. + - Позволяет оптимизировать запросы, если известно, что результаты на некоторых уровнях дерева не повлияют на результат запроса. + +Реализация обработки запросов от листьев или от корня зависит от конкретных требований задачи и структуры дерева отрезков. В некоторых случаях может быть полезно использовать комбинацию обоих методов для оптимизации производительности и удовлетворения специфических требований запросов. + +### 6.3. Изменение значения в массиве, обновление дерева отрезков. Множественные операции + +#### Изменение значения в массиве, обновление дерева отрезков. + +Напомним, что запрос обновления получает на вход индекс $i$ и значение $x$, и перестраивает дерево отрезков таким образом, чтобы оно соответствовало новому значению $A[i] := x$. Этот запрос должен также выполняться за время $O(log n)$. + +Это более простой запрос по сравнению с запросом подсчёта суммы. Дело в том, что элемент $A[i]$ участвует только в относительно небольшом числе вершин дерева отрезков: а именно, в $O(log n)$ вершинах — по одной с каждого уровня. + +Тогда понятно, что запрос обновления можно реализовать как рекурсивную функцию: ей передаётся текущая вершина дерева отрезков, и эта функция выполняет рекурсивный вызов от одного из двух своих сыновей (от того, который содержит позицию $i$ в своём отрезке), а после этого — пересчитывает значение суммы в текущей вершине точно таким же образом, как мы это делали при построении дерева отрезков (т.е. как сумма значений по обоим сыновьям текущей вершины). + +```c++ +void update (int v, int tl, int tr, int pos, int new_val) { + if (tl == tr) + t[v] = new_val; + else { + int tm = (tl + tr) / 2; + if (pos <= tm) + update (v*2, tl, tm, pos, new_val); + else + update (v*2+1, tm+1, tr, pos, new_val); + t[v] = t[v*2] + t[v*2+1]; + } +} +``` + +#### Множественные операции + +Множественные операции в дереве отрезков относятся к расширенной концепции структуры данных "дерево отрезков", которая позволяет эффективно выполнять несколько операций на отрезках массива или последовательности данных. Обычно дерево отрезков предназначено для выполнения операций типа запроса на сумму, минимум, максимум на определенном подотрезке массива. Однако, при необходимости, можно расширить возможности дерева отрезков для поддержки нескольких операций. + +Примеры множественных операций в дереве отрезков могут включать в себя: + +1. **Сумма и обновление (Range Sum Query and Range Update):** Помимо запросов на сумму элементов на определенном отрезке, можно добавить операции обновления, изменяющие значения элементов на определенном отрезке. +2. **Минимум/максимум и обновление (Range Minimum/Maximum Query and Range Update):** Поддержка запросов на поиск минимума или максимума на определенном отрезке, а также операций обновления значений элементов. +3. **Подсчет количества элементов в диапазоне с заданным условием (Count Elements in Range with a Given Condition):** Дерево отрезков может быть настроено для подсчета количества элементов, удовлетворяющих определенному условию, на определенном отрезке. +4. **Операции нахождения k-го порядкового статистического элемента (K-th Order Statistics Operations):** Поиск $k$-го минимального/максимального элемента на определенном отрезке. +5. **Сложные агрегатные функции:** Различные агрегатные функции, такие как нахождение среднего значения, медианы и др., могут быть реализованы с использованием множественных операций. + +Реализация множественных операций требует изменений в структуре дерева отрезков и логике обработки запросов и обновлений. Это может потребовать более сложного кода, но в то же время предоставляет более широкий спектр возможностей для решения различных задач. + +### 6.4. LCA. Метод двоичного подъёма + +#### LCA + +Наименьший общий предок (нижайший общий предок) вершин $u$ и $v$ в корневом дереве $T$ — наиболее удалённая от корня дерева вершина, лежащая на обоих путях от $u$ и $v$ до корня, т. е. являющаяся предком обеих вершин. Общепринятое сокращение — $LCA$ от англ. *lowest (least) common ancestor*. + +#### Метод двоичного подъёма + +**Метод двоичного подъёма** — один из самых простых методов для решения задачи LCA в online. Он не использует метод решения задачи RMQ и основан на методе динамического программирования. + +```c++ +function preprocess(): + int[] p = dfs(0) + for i = 1 to n + dp[i][0] = p[i] + for j = 1 to log(n) + for i = 1 to n + dp[i][j] = dp[dp[i][j - 1]][j - 1] + + int lca(int v, int u): + if d[v] > d[u] + swap(v, u) + for i = log(n) downto 0 + if d[dp[u][i]] - d[v] >= 0 + u = dp[u][i] + if v == u + return v + for i = log(n) downto 0 + if dp[v][i] != dp[u][i] + v = dp[v][i] + u = dp[u][i] + return p[v] +``` + +### 6.5. Декартово дерево по неявному ключу. Интерфейс быстрого массива: Доступ к элементу в позиции i, Вставка элемента в позицию i, Удаление элемента из позиции i, Конкатенация двух массивов, разделение массива на два + +#### Декартово дерево по неявному ключу + +Декартово дерево по неявному ключу - это структура данных, объединяющая свойства декартова дерева и быстрого массива (*cartesian tree on implicit key*). Декартово дерево основано на двух параметрах: приоритете (обычно случайном) и ключе элемента. В случае декартова дерева по неявному ключу, элементы упорядочены по порядку, заданному индексами массива. + +#### Интерфейс быстрого массива + +1. **Доступ к элементу в позиции i:** + - Так как элементы упорядочены по неявному ключу (порядковому номеру), доступ к элементу в позиции i выполняется, обращаясь к элементу с ключом i. +2. **Вставка элемента в позицию i:** + - Вставка элемента в позицию i может быть выполнена путем создания нового узла с соответствующим приоритетом и неявным ключом i, затем соединения этого нового узла с существующими поддеревьями вокруг позиции i. +3. **Удаление элемента из позиции i:** + - Удаление элемента из позиции i подразумевает разделение декартова дерева на два поддерева: одно до позиции i и второе после позиции i. Затем оба поддерева могут быть объединены, игнорируя узел с ключом i. + +Декартово дерево по неявному ключу обеспечивает эффективный доступ к элементам по индексу, а также эффективную вставку и удаление элементов, поддерживая при этом свойства декартова дерева. + +#### Конкатенация двух массивов + +Декартово дерево можно использовать для эффективной конкатенации двух массивов и разделения массива на две части, представляя массив в виде декартова дерева по неявному ключу. + +Для конкатенации двух массивов, представленных в виде декартова дерева, можно выполнить следующие шаги: + +1. Создать новый узел, который станет корнем нового дерева. +2. Как корень нового дерева выбрать узел с максимальным приоритетом из обоих деревьев. +3. Сделать корень дерева с максимальным приоритетом корнем нового дерева. +4. Поддерево с меньшим приоритетом становится левым (или правым) поддеревом корня нового дерева. +5. Рекурсивно повторить процесс для поддеревьев, соответствующих двум исходным деревьям. + +#### Разделение массива на два + +Для разделения массива на две части в декартовом дереве, можно воспользоваться тем фактом, что приоритеты узлов определяют порядок элементов. Мы ищем узел, чей неявный ключ соответствует индексу разделения. + +1. Выполняем поиск в декартовом дереве по индексу разделения. +2. Получаем поддерево с корнем, у которого неявный ключ равен индексу разделения. +3. Разделяем это поддерево на два: левое поддерево становится левой частью, а правое поддерево становится правой частью. diff --git a/doc/theory.pdf b/doc/theory.pdf new file mode 100644 index 0000000000000000000000000000000000000000..404281bf1df61077a0b38cf6f9176c5e6ebe3230 GIT binary patch literal 387856 zcmd?RbyStx)&~p%(%mV7w6NK9cXxNomhKc#x>4zFLAp_pmXb~ZN$Kuxz6~6Yde6D% zzTbH7_})K0hhwn!v*%iCt~uxWtuObO&* z<75xk23WCTHr9@}e}JTS90SCdNbk7;h%uAi^8*lLAqC#e03gOn%5*nRfEXL;{pbKO zc2cH0KKb|nqOOkO%8rJPAaIxB%HZkU&+zs$kQDs?PV)c-dz)vxAXi6# zq!rktgzos6kXR~J@C2=8ezbb}C4glp}<#yZA54nk%SvrF3 z0b-WmnTUX%*%*TWG9YUcM^jQJc23}JD+fn=kf9Yaf@^Y{j_lioYV>;cft$}?o(Rxg zzK3Q(s;n)7v6p5NKndlgkL28XSbKIOAfXoVR4clMK57E;x!Y!}s?pxwNC8=QBQJlM z_ZdGXE|oLWwmx_8@XpPvSLb8;a*cfax(&SiS5C`RhJl^Q4Oc5?!L=LTg43e$dOGc~ zXKvbbk6QRSxnJI-)h?}Z*Dapz2d?Bi(#KC?U9jXSP;cktZ}dwWs&_KffjdFkza+bI zZ@!snbZ$KGEZHuB-}$sL=WxwD_1(JNV);Nm$HvqBRZY#x)y2WEjXqI4haDfIDv}HV z8?|I0&O_*y)8V!LS|m=?{p^dLj)&ydR<7zacz}w}x|Sm9va@9T#(ldtxOG`6u+<|i zS!$`)C9uYA;cUK6dIQ6xUqWy<`+#{NG%bO4Z9_BYYl--<1lb6}zKq9m@#1q|g2n^3#TK7wzk{#w$$f9}03^;CQ6FG9re}D{1<{^0tAu*za>V+ok^1!d4 zbT?r|;b!=;ckgA7zqtR(>%B`OuV+xUM1c?`dJ)RK;}EGbtr39RW-Z*?==YJ+wmWbtB-_naOKdOjO!|1WnG8Bk!xR<#f;t5( zOur&xj6!>63rjA*$?v7MH^~|j^Wngf4GCP58eOXDlfb%$hGi4KqJ%{XeDoal11dl8 z`ZNL#()vX=W?#SEa-II!rX;k-`$4LwzVKL+8oqe;!!SDJY@`oyu4KOCTw4_$E{+XRLpSRNh4!r$qIQka$5TTz!Mx>m=mL>_r~t%GU!k0fZEnCRCQgZ4c8- zP!Vz?RBiId;=?zl-`2YpEI!wPGm;Tm-qj}@Vfa+>C^{DlZip4vehXW&+E!%ubtBP6 zwVB2kohb@>8U(ahodfMgex;A^PH`^0SR%R40d!3}Jk-F|839gm#(9U(W7toKAkg`V zx6s;F@;8x{EcX1VbSs53bK2hcD2c5urYPd9rvam^mO$2B{}egH0`mg#Z)5?^9Vr@V z!`(~jS+KzxUj9f7Rx$Q#Xf-k|)lX8g=3ZY_zd>GVo^?cHTmQ&q;7d@0N!XNRI6}2A zmz=;1@>hRs(gkBO?;eDkib*?T8!BMR99C1N&(0FYU2TgvMIZ z^`2NzJA1>PU4iOTp6pqoddVnep;Sg=0rS?v4#kK(Hk@;^(5gdaF+g$QB_L04_90w9 z=aM*_Z-Y;9P(G-_W87H40XJfv&haA4HdRPZZQ~?o&LzeS*B(z56}R)id(k!V3hi*F z8CfQEMuTXvtN|wriys%Yj#YmS2f4e7;T3|WjLaA#t%XvtGG5WdI=a}B-WoE|KzMAPkwtedOvnb8kb7MSs-=+;em4WUT=;K310j|0+F7}C}*uYIG*2oB=L zY3#7wxXiQ~tcSAj$^6A<)8Z;0&f?ICcDn@w21;(m>2C{!FWyu#(3Qe2a)J$Qh4bsc zyBB@)1JKsc5^#m0-u6BZK!p~|^)gu{UFhDnZ6+7#L0t|;&6Tn#RVt0uIq->YcHyKu zLsA^eaVU*A-K1%ruS5N)J2d2wp``rQexY8QhSmyVQ#)S3_7?9 z^f-6rzj}&Pzoez8=r1-D4evi0vs%dt8Csx0=gu>3{Y6i!QB@vtkhPSKDje6D3+6b# zh{QVuZ=&LW*4uIqj~`2Q{?nm69`4yntkXe=Msl}#M|C|T%@&7D-QJ~@ff9$JI~HZZ zM>m5=yqYRU68Z4L2)a(C<%+4G*s#>#cGA2M(qNVeM_+)V1y_{CcD6d4iiSZDOA1uX z>rl%O^_57?mMziVtfZ{=_;C%`p3ePuqkrdp#p$4K}o2m;aMF~#BLzTKzL;4_pnVTVw*=UzQs47=#Z5rg^c0~Dz;2L~#pV;}Ei-Kw@XL8L7NxGz@=~eM0^*ziZ z^fgVH^#@`{d=H%6_{fFE!|Dk5yd!#rFt5pwK3LQReF8y(dWLkuFQm(@S@dE7egYz; zJP_pJq|GZJjx-HfGaSm?6H~j?59#S(oZwdyK>TIY;>$;p@purBBvyFD>L?y$lsg@u zg0H92xJp*->t(%JOh4GxuH|A5JE+S3RnQS#7?tg{T=)Ay*k<{ufUzfy^@}glOXW4_ zR>Jcr(zg#>s zIutBgf$bip6J<52*Il>v&QBzBN_ln0@xmc+zy@sV=h*>ol$}925QT5LW!q6V=(a3b z^$fP6Q+L>Pp~PX)wAfbP321y6f%0n|n328sqFI(gsgN;PTy()kn(=7|miHlssx~jF zAQCH2PPFK(GpTRZeyIdy;oeImtNPjuvMbAda+Sr9jAPnJ0=G)XljRLqL~qBDEF%wD zyKD{_v}ah`cwL#r^pb`gn=Ksi_nE#9D2)P6=otm&o@VUT8HSaz0NCMUFDQSXO>D1yQ#t9C7VhZjBs+9AOm0 z?BXY(3H+*;r6Bw^B)WUwz)YsY#&_9@E;w+;`(vkP4TX{E@@P*Z=rA!oYUy1T!M_fpNH$yEM6jioU_)Ea!aHnTPXNE(Ar z^Jb1O=q13%bcbi)Aq=e@ZyP$0-uXBH6&qD+v)hw6*gd)PMSeZI-;&&MqD^||aOsl% z^e%57{NVCGaPxz8VM&qOp>Jovbnp4zO#%E^#YS9GMAp#uPksR+>R=y-kr4p?QTc^~ zBgjh9`ne71oj-KvTiwpa-ptm~#-8-vg#yzv|KSMzWb&5&2b1#l#vpsJOsIdz1ngKV zflSQ6;(tLc2zHr4;0MZ1wzifat6QIr@mERQj`5?TKXvkxhdY+!46Q(bzt;!Y<1uj{ zWo80Mf<1k+XM)xymLO6_fZ(l097#Dj82`|YBPl%-6B{@nU@HMKGck1}WoKsr2s)dn zn;C$gHTHPw* zKeiGU{Fhd@UifdV?#$zd^{Ci8f$l_qCucFH-`0TGZ)*UoRwqkC`=84|-OO6h+QID4 zWpUf_Pwjyx4jx$vAZBLo;3#ZrXn(6I8N)w51DRL>cOnM|S(xt@&2LlvOI+ahzJIff zzvuG5V;rm;tiKoskd@idXm4ito6-FTB)E?V{u?9!qrwj$xDS;6x*G0U{)NTA)5Q;-{#g~AziR@# z6aL&3m^glFf`tY6Qxi;VY^?vJ2^o;#?XdtH?)_tbU}3o<{{sTRt#9?hNXi7>dP&)t zIDQX--i0XtGR2=-_{+&e{GUEJ|gu79GO!^-I;ol14KUn4e z@Tl_t{cdy@HUG=he;ViS5W)5nBEXW60NW`zEn;s5vX=w7C@BBBOZ_Ln_yhj`JUW0O z?U%Cy_{*P=@t4yn)30af|D>(I;qjkU#Q|RAw+487!lVasaQp%wY+$_pTsVC{glU~-FG z4)lLH+x-)Y+`9(993xonaN;+1{yf@)b76Pq-(OMW?#U0U`V~d~o~i#Hiro7$|B52a zcZsT76k+)birlsM!Rr4CMeauV(bJzA`Uz%tT>bEN{$3FO!8AEJ|1?doBl3%7vak7T>4)h4eUguvnS=dy>HP?JxF#>@N{2rA zgxSvo@lqIIarQeX;&b<3}%mClH6 za1A8$eDSIouGTYP0I@;hqLJxve}g|Y?Rx*ByD=@zT0brAdN+f?kF*D%e{*&zvgvdx z#NbDp#Smil1pEY4a=u691K__ILwRw=c_}%c#?Qxpb%fZSsC(mlcK&50rA?6r=$Ux~O|#Y5@#3Z6c0(O_p!vrPz&o<97(FklDAty3M) zs=1`SB`j#+Qaw$ackV(3pp~ntu6=r}-iUncKX5qsMKud1c5c`)Y8$cZ?RTe&O1lan zxGb8di>P=>c&%f`lt8=CuSk@7t2`a8k65fv={K29Y)=T~i7P}=UWc{S;L&*ifcU@! zErY@hMC7L^0aO7rnb6Khnut|H#Y>_DzA}gk`B9=;UXjjeF4ThalJ?SG>V729Sm<)w z->e!ML^RidI7>OWOdP=xeor<*G<1#-XSPyLI@6@=L6FdJc$xT-SgtQYzTr4QO0^9H z+zhiQC5^mTZlaW93uf`A^vrbxh13Ahq`LA~yI)DP)T1n0;@vZ(DJbQC{-MgZN0 zlNEna(6(4)M|Q}Rp0Z!kngJ((@?LT%afZ6NY3Q|imgq^y&F+J$vz~m; zVg8urAQ`E5#`cgVazGk1!UWS98=I7e^!O_2NXx~zM(8AD^I;;d6V!hBjBIq;5BT{`>)&VXjXi(umhkI8!P zRu~XbMEzBw7or?GHzKaA7PGA)nj02wBjmJjQD?X8iLTH(2`v!A-1sx9le`A#)ssC) zRj#NYJcDQwTN02QWOB<3a3hWqQWpP%!c;YJb2lHJk3E!L>bo6up9`0+WpxGC?q;En`1+tjQdFyQBV`=Nnpqr2luuqq{Io5bvgm|D$;bk zIKG7~s)a+j2>Gc)r*nS-l=&?qYdgJZH6KnSEt3*@o6qoRHJ|d-C>jJgi*gO&kL?U( z|JKHhrq=A{h)jpAr?G|RgR4f7A!ta^vO@+lD|Mxo^k$rj{=p~X;H$PY8~O&jzH+=j!6qDQB^NZuEl(Vox2NXn+GNBDQ%=@Dbkc}5sElr z7y`-TyXOvfEJ6)oN+Ujh5JA9OgtbqWZtak?uamS0AOgVkmhwpYxjp|v8*w80%I~1s zPiz8dYMZL9IkQvgxN2TrO{8sk4@Q|t@gY*?)sx6{3^OM&j67L0A&k5yhq-*(G^N>J zNj0ZPzL%gA+9-a-J+`*(HZo#@9m_Xv^g5agK7LSq@LsSjvaF@s`v|q6b%V97Hp;I? zHIq`T?p^(CJp0ndqIro|H%zV}!#)(^BDpdqUCtMIM%GC`%J@W>49L+ZQ*m>aQ|dUF z1nx89EY!ZUecO%#!7`t>PqG-wKd&1)0iN<>L)q^rqSR3qB_Kq2oVUsPdMpmwOYER$ z6D&tag{yV0XZ2cN($G&F7O&eKu)^UBfk!ASv{A2Pfs0Q zZT!@r4}l}55MfJlr7M=Hm+13S+0jFMXtkgq^9Yl~c^%EQ(Q*#T_<692%j|(o{Bew= z5}m*@N`mj_pslU%(8(H5*lGSzltuU)G;q8jm@#E#5=T-Sxq&fq!j9P9GetF!h0)3# zn_GL(Ps@k_CJ$p1E1w$#J6&)RVmBbj1*>}Rv13tD=9zoJx-LmTWSy410kSuByF=g) zl?PJ^zL&IkXe?26rAx?-{gn1B()Xj2ockE)&De&eS_X+sMjy zC|gRBS_t#{E=fcTWCo)^9R@X^J#)=9t@8eivw%1*=a@|q`M@CxZTkb1RMViqT%}EJ+X=Et;4p;0=y*`_pC(U=nLtfMJg)4mw3 zpEwpwVV$C7SW9O;bHqq$Z|T!^uZlrO)0}gV-K1uO#3(oG@EJ&{CW*i8%xXAs_A=Iw zOTOUMD6aRUi)C^YLai9=MPD}eyi;ETyIJqI6Q6;17Yxs6=`X@#ZgNM_L(5N^#Mc&y zKScs@qo+G*u}RfhaF%NQ`8A;>68o}x8nO;fZTT*d=0)0#61pP79t$Ls`Rx@ttu-$R zpiiAK^`XZ$HE|i>Tr~28YJzQnQCiS6GaUNZDr!i*N+?o^g~+!n5E)?8M?(R1n!`dwfT_!}4QAVj{>-rKz3!C9;uNqx{TRB$O zLX*~`Qq{65m@J7wx!A)QHq!#U`3pgm%_d$yDUjBhyUvk<>|&o@>+++?bt+=H)EIwP zCq@_S);FSQO%BV9rr(#hRCPS!_t0<{sy;l28zB3t_ zr};~f(nP+&_au~0Hp=MB)F{qb&L2NBc&lDJ?}+lBKT>suNM$vB6MuT~Me)gc%J5Lc zXBk~2dylD*%5}#41GR5yN`1&W`!t}YNai_K%`y#TVP=i=D1t*4$zHggr;e|DO?c0! z=1{0@{6&d;r&C`xp@-Typv)#_Q9=}_AKW;QVmaEOlKG0%1b_~pe66^4?LPbTNoXjr zGn?jB@fZ^_HV4t|Y^1uNS=X&w5Qyrt>FmEFu(by1reIno%hZp&Hr!lF@OtBvxN|l{ z#EvSbV%?$taM4&%6`yuxcUe(Eab?WJO4+e^RaiMMIMYN4->lTvFnbpdt*iVT{=5@n$Rh|aHY-W-)`6WMBWtZ&rg2yk*kBX;rL)esBLEyy} z(Q4-8*Uv{AaOH%u-rZ;U?Bg2S>(!rZ_|R^&QEuZ)w1?OIu1!wL_P8f`s{%^%V!nsQht(curEi?90Rgmk=?rQo!0Fc}}V zyh~79-oSveqf{Dq9z2oMYtNq;sz|SbbNYT_ci_QW<;wssf6ycA%9#984eoWV{BRC` zcV{@+RV^(vg1OYOk#{#F)Or-<)R~z%0skFq&c!=Svvag zKCEJ91a~&KEhs9=o(9(^RWbO?uN}J=8lt$Vn^EL2%tekQVd1s2o+p#*tR^cZyY=9P zza<>ZsOqDN-Sd<-g$(@L#POXk`NIwS({KNW$cof$+{@6)2plArHFPxnAEjpQQ((VF zW?BA{6uYCjV+0&ixeJi}7E}2(?DcC*o0YrwR3uV(3t0|n zf-=VD@(t8nl4$UL+|#d zobN=}d*}A&;E$6Vwh#t7#0}S5M=(=%mk_XMP=qMHLgm0#d zD45SVY~M%UcrqUEc2~{hcuJtY#OHvJSx%BNQ*I5MefjtpHzfmYl!eqr+8cu@qD{so zRe6q#N+{8^+wVQG%J64=R?Oylo<96A#B@(s z67K7MW0yc#6ZWf!;Gh|=gn91IFNx}O_x-$;#w017-2vV7_HSsD;kg_%#h!S->DSXd z<9y)n)@F;NPp~3le1>Kt_}rv$gB~W3*o>aLk}s}t(PR&no8M~a!*rn;qlz!OY^_Aj zXNosn@vx|`2k;&x6&#W>@jEAnf!^!$xcTq}oZ;vSdvNWLl&9?}lXU5PXTz=avrld2 z5W-*CWk;uN_|Szl&X`>)GUkp-REu(I>I>^XFyu9Y^pSr$j$H_HUQ7!G6^W%W*C?i9 z?xf{a?*l*QRX-fd%HFH(Jl59)33{`ept1U&Qjxx;%JWd}HZBb;vST-&di>|AHaR4ju)vyi%6nLrwp_hRRF@ z^_;8go~9WAq*8AUo~n|3l0IjAQHJOS^MV30&_!^KInW!w?2J$H zxl?iQjXQ^2p6@$x_N!{-2x3yJsdprmtTV>+Tl0)V+c z;uXMGsPHp-lzzwE%9Mr6D97qwySyZIL0AlPlWohdi9j}AO2AAqaL?;!f7cAdZ6(q+J)3eLVv&E^$M_+xPkhBr@?3&5DVldZexHCdQ zPIrgtfBN(eksgbqlh5)cZXkPKle|~s<)L0`sAVKgXF_GGUtP9U6H_hpgy7_%xAOoS z+$y>irul@l75O|*!sNs##&_l_ERWTt?wioKU z56#xh(XMp|8ZNXgm$&`-{Ci)fzn1?>SkvexFAp4-kTW4x~ce8m$$AVNF?y zF4pPM%tr6`-wZ+UMFc$~Dc~l2tGwjO?9YDmQ6NFf&s@sK*qM}as3uW~obl=j>&TZ0 zL0Q9e_mQu}b%>V)bW4l$?Sq|G+4xnhzAsvwY02X~0=9G@Gu0p9t)f#5Jn2-kxGppQ zO1~r~P4(Q&eRF?7b&Ly9iC>^n8tt-1PF(=TzwqE{JhPttgPVowh<=5CGGhsUX2w&l zmIa+mM|}zMBvrY(Ip!}En*&G#blN(hmI#lfO8T-z8ZrR6IIJBOmr_>9 zBg+i=Xl-vVBUU0&vJz2VErkw79DSY7HR?A$lzJqJ@-UNVgH8iImx>P+;d9S8By&k3 zKC>L-;s7&^%=auOxgC+Y9+kzpy6dI4Ev%&u<~v<&InPp%-lWCoK}B{Zpsd*&io9h` zNr|>3QctbpZ8gIbJe~{ts{QOOqj@I!F)$>))DjgnCAIYUba5zcA#puR&9{@AS=AJY$;u9j=HNt_6GRY@ZYsfJGLYFSHdu;Tj9L1qOq{fA zr^#wSX7;xlrPp%RVO`A0dJOuh4ZM8PvrI^)-Hb*XIYN*=mC)GGJVvFV6t7KNvDNy~ z^l4nc#|KKI$tmkTLGrP}!ebcu0q;K`ZAi8xC*_WBUD%H}(i3%^Bk*qZR6ho0X1;jS zx@xrb7>G9DX>hf9(|Nt-b7pWk!JuU{WpC-RYxf0{Nc&L`pX?iN63M7ZQRZ|9x&&}> zEy@D?O2U)ZSYq9u!UtquwO3}>$4^gi>?kDhS6)#$kiC<;I>P_B zSmUfvwavTj@HRBIeP0&_I)yA9{NBriYgkcO-O~JNq7mZ-IT4PWd{uy99n@Tnf;$=t zLuNJ`Yod~bhSZy&Jd9&C+1F)>%eo?|^U&?U5jK%MLFG^& z$C+|2v($Py>AQShbp1Q+x5JbS1OnM}cuvH)G;138ecG{KPdB65<{$!97+&z|dhHRI zE8Cx3d-u_H#$F$nPmI-{56SWCGZskOG}qzr>*wrAJZ=bMO<`5pR{8WIj0QPSrP*cC z#&g=qsTI9ZKg)dGW6M(5|AYo|ikm=pT$w%BZIpwuy$-!rrECx#-FNDJ@rb9F0- z4Pc+g3LN&C!35248m5aU4+m$_>}JLWf?6UeR2ca79_}+oK5EE06Yf{sXk&OmU>w!@ zat)l);O;7cXQ()@a0%hpJO8p@q)@;Q5`T{b93HK}e-%@%HM<$ryZUS^ythf9zwfrO8$c;0 zgyfyCS(!X7s^kbjnTluQZp(#;YhB*3#H|eDyZZVt*#liJJsi<$4F}+y9QtiaM)_k< zOVXxb!!{XK(Yd1@)>Fk!st_NuaieMC4V4{V$6m5rdyE$%@ML(ovgk|7adJLHb!}o* zwJRSvw&%Decj|UZ>E#}o1zOoL4%CkteX=cWjWg8`U`|X;9D@nTcLStUi19^;e0jZU zY8tp7K!;C^{l&!6tKxykjuhj-vqgxQhJKeEZ*zl{i{zOK4UhKsmDsx7vb4Eu!|}-b z2`)-6bI0~HqAbb^zAj4I%87e?ZO3R+gq4)|lwFA1U%SayeY&MVjKQ$55~`zU9yIj{ zDFm5NGbaC|A=dDW-;4f@by3Iwcz8b&_=XBj$hGUjha|2>J)#Q4OboT})Dwf|oDIt_JIlDMNpD!J+Y7yXe`P%l)MXRKZ{$I5^m=(>X5i?T2nYS!`43 z_?psB72_Yy6)4KK%c{1G=xB{cqe9{k4?#-+MsJ%*-tR z;sNRY^nk=&yq;@#NHsMH@NdHSESA*Y0Lgk*MK%|8#fdJMs0=>F)X2-rNbU`>hYecXPD| zIpuWg1I3Q}#y~L#_TMH<4kUHYC{4;&n!+wYALG^Wvd^-;rgI>3RSO7Us)2VDZbFj zU?pt-*iw_p11|K&9hOuy&vc{ida|M>TE?*kpz=LH1qi}kN-J=TUx%=lJXWN?oQ%uG zF}cA|U1O<1OpfGP@V|U2KdRqa-vF-m0C*9W7-pzGu1o)Fe!5kI!55?WvGFmxtt5N< z@^^Uy<&u;{M1Y0B6`SJ4)im3>v+!$GAtrdM6&KNGoF>vlypFMOHG*cS{d&8r zw(~a7s}oLggwIq^uXHp?nuLiLm%=S%sm=zB+!#ynD+XPrt;&TwX46W7_q7cZ! zOe3_ALF&e928Iy4Wbnz-J?voOHm)OUcIiD5w+c~@(Nb%yNpO~Az<=6%KAOOYE$I7P zsG5eoY4bz6rwyjcaM57zX?c1;4QoD~Ap1Iw-&xNqe8)YeQ^lkh1lG>pSC_;pwgT)M z((>BMKjnzd^{NgXK2k>fw=Lmk->`wFblV8KC zJbWilevI(@XTk`tQ-VY{5p_o*O0f-PxGA~b$gwB0g+_E*$y9Q|ww)$jwDW&1z>L21 zMdP@MV+v9>+my!-jAEawSZiX_uP~L2UI{s5c|s!wEC_|%k@7ZdOfP0AfQ1PtvQySp zNILD&e!%jCs>N}^qGh)esLjeovI3eOTzEC7tg8k)o#ErQ)+Z(beI+PkiO*#Bi0K3I z753|L)`oG(clqURrqRulqFY`PyD~#RE{)u2VJFM`dRVRXgm_t$S$w9YW@Rjh?qE-d19V}sbIP)~?bf0Y* z_HU2@^X032RZXMD5xq<+vJ=nHSPN>#a6wknDYQipU1UAn|=}`qe^2!)(dq&w&;hcB0%sTIGIzuiL z37VkGyx>`6A;v=xYm}!%?rwX31y*mMWeyMvYNkt*p7Unn=~ z=Xn;Ji$Wjvq~vnG<tS4zz;-+61Mkl2e)1*tcv~THt%5($T zcUPwh@Ipxr>(InC7!?$;F=)pVH03;ZDJumpU>q?Rs~(q;mv&{QlDa@mPa4df&;|}I z`d*4k0B?j25bE`pm;Nbq3w13dn;wD+?#^;B)17_dm&h==qD0U3OVjeptO>YP%~zJUwrCf+3lT|2vxki7y4EW# zw9lGvgyZm=Vd0#yFq_88;F( z&DOji(83rlAE^NfKOb>jAi%(Zhm}egu(y+J^&so%jC8#yFDaelXQ(jD^n+CjU{2%| zj;y7qRZVo&lSknk)rm~3W{`VfXQ__xk3nMz%8od=Lx?1H0`eSaV;f9pl_O?LKB8YX ztK?*)(G;f6`KB_+yLAeuT{&?xjGu*Seum&wBQH6|D<#e4eW)PlV$@_of%%3qp^CX} z%4&IxS%X@dhIeUH8j%#}FYL@9NuWg$;F-TT0kvyzV>%oMA03h#YKbS2o&_&jVmMS6 zO^`uPks!p%@-Y{#E&{DPmvW*nM8bEuMTc9ztT;xv1x+5Hli-8kDY!gV^UR^~%T@qWw5&esPw) z?E`T;_DFbF=%hF+7(j9#qE~`2xsP-m8d)b9Kh4)-q`g65V{ra*sXEmfrK{4Euoyd9 zH;M(%P*YSbNGl3~jJfQ)pY(Q8E~D_1#KQ6dso>e=(g&@Hj1(<`oTrG9TGr@HA+M#d zeXJ2gdxk4`m~Ot$!Oxzy3|DYnuU$|{*j%4NU!Fd?+3wd*YhSQwtlZ#oZLk*~BNS6i z7kgjj(w6t;V=~>6w0!lDoqKwSG^}}icG}>pE~~E&NjZ~f$}iT;iQ!{KPI<~}A&0wk zTKV8G5ybtvLLbMTr?|2SHT#I9nDJU{_Z2F+W*;a^@Q-xJ<>{vaA6iVd$YwFMzI!uA z!_qw%&xI1Q zxhysE-yE%t6W#1(y+z*BH`OPb)DVvA%q?vX6s-*(1TSvmXoQ-<_4Q?}UfF^ED{Wdp z4_l$gh~aq>$_T-tdG{$pxHj%rNc-UQt#ECAVaS3fl+FYG=?3S(E%8W0v&_Wc7GhS& zrp^r51ieM8Xupcq(KUFuPkIN*`^=nIMjaMUbyQ9^qasY!;JocoTGsZqiFO`sWCMXMYKAMRdP6!t% z5kLXjC=`bJHCGxEq$Jl2EA!S-hB?24U^U=XYkD7Qgk;0UtY$XOt;mSgil>kvF(7=) zVsX4V^+38xf6XzEEYAc%kH;>W=3XPlI2t+Z(7yK9gclNL#Sv=HLTtc)j@O<}~ZA%`48cY{N8}pz#U5%YDB$ zJPM<_l$8txzWSLyY^T{6c#EtIJo4QgXB;ALk!0ng6c&j4_Y^%4_K0R99od7JURh;r zZI)C^Z}8>9neQ;*LVs|HQyHk=wtv;sJu(58m#pzLIjFdIh1h)YWsy;4kd<{&Y&;vI zkmDAJ3C5zGLta&%g&3JFbjil!oC~p^CC8>S9&jOW$%iDo`Nd|pds8V}B0tD_V5Psx zWBJ{ZqpO!j73HZ)(EbbsaR#sKbO4!8PB@?+iDpK+|MBw-N=`8b82ggo;FWi#&9k{x z3dSsSRP!dUeWRKo3Vr2Q+;iaeC#T6(58*G3+Ob`)DWmdiAfXRFtu(llX;*CP+HPYA z%bYE@0oH@BT6^q1pp>s#Y{Ky#cvuO5fT?9wDJY|gAy!TWJHwK8$**XHm`2DlfXh$Y zZI>JmtS1ekp!T{K(fm#0hE=fe#NO1Eyvq4fNQtDhwYq;H74rtx=N0E zz_Qe_JT_(c1_I4A>*()Y&|fc>+$DtnyGb}s()-*m_%%n2q(5?~{|A-K_r>u4l`PzC zS^W>E{onhbtW0;egMSt>*T>3QObVa{>^!&%Ly82ne(T-2oXP3DWlhxCUh_`1%kL;;Ijmx4oyrh18AEqo% znY>JEk^-Jto;LR(>35R%4>(en?O10ar&uYYa{DW@Rz=e)aCJd&w33eaB1Nza!Be&o z^VhIEv@D;9Er>X9F~eD9;AW(?%rX;TMhhkR#$Vc+yOq}?1s9t&DquUriTjx^&Eue} z6%Ols3~S246jSg)!jdc9Cy{*gRkY+?2ayd1Un|VeDX4Q1BAx0vQNJehgZA-aFEqx| zIbS?l^CHMWnN^(-wsF&k@sa*vuVSm2-fbb}#iJSQz+15;3=So3D#*#8`(`Nz+eX)a zL69GM_lO?gz7#1D!S;Oa$}@=uSKte(QGVfv2g(i^%n1_-euz`qxQlz*Hm<~i@wOY{ zC5fNyjkw~rjLhteB`25pao8sw38gXupz>lQ(=JJ4KS+jTp3upGAma`GCdOXxCzzFd&&n~Fk+1o+G`vsj8hxc zdm|rl6dO`5_C6VjaiQ$%I&DK|NylhO6}!38sR>NIViMK-R13Q2$)l&VRO;6|bIquR-&okuYb z2(N`SI;cOu!gK}bUmbtu4M;n^z<9O&@i>A3k~L6&FK3(&ug>^$)$UlMHQ&|V@pIgd zM>&&8`u$UlGY~Re&QDC^sp{E821XYGcGymEi=dKhmDy1>vU{MfZ4KAkJN!clR6 zU)>!W%5Qb8JHgn2FLIS&sh%u-@reH|(-hsMXCce?4Pp9VM5LZ=0UeE)}!zmyT z)5VL#qcWNH%?#uoA`>RU{Ck&cHrkUqf&am66H^%Qswu2x$1VK?38QOywhZhbeYFXI*dtJJVmwZ;Sd=gS< z$WQdNoyBUkI4DSA7}bH0Che31lk`>q^WzTlicYb}P_+-K$r`CLWCcpkPqtZKG3MK2 zInk{Mm!Nw*m`>2Z168Ww4^99y6)O}Z$SlYsT*qUGLNzI!u$24Ixz^rl_(5&uBuw^k z3nlaFP%9xLsB@#(U1UlRvRt-p@@cv5MWxZ#vM_yf)RH!TPBLOxS~=+115`_V_tdXB z*qrvTxj@7pW2 zo-_RMwIykuHB~N+FB@Pn^*l;lk^pl>CH&X+JKNWG_!eShj9qdw$m|uLlP1tcJIzB3on}R91z>DRl1h9#o1gd&1pH zEyb^l@{D1uEdt9j=+*P5XD1Sd)zPUr{!4uYFVS_nP9^Dkl+t^nW|dY`@TU=wSa91r z_mnm&%gcR;;TV>LqL=fAa03SCl$d1_wP`!nn^nw>)0~~7_|YMc%Z%mc-eQDNs4DZR zQ`kEO+P3DcF1az=dpaY#IlSCiiEbAOQkDnAyL}3G0M=nOGZxXNdza{IGI*u|S_X*kr)?j`ygl(WJmynzg@G$Xs-n-|?UEtU zI1ZYyb?x+omh8-9ezNA6r6BOyJ1C90a*{;A@z}!)H?AT?m*pTXQc$>L6S^_u>?Gl% zH2*M-;KZZq7Hm2;RYscp?Aqyz!_O=GwOb$6<3hx#9l6CFMc6i1g+HD5op~}wjY23f zj0`+Z`JUiZP7Y7GjkSDck0Q9UIlR`&zR45p(P(v=O_dok4H;6WuB2ig2J{(}gl0%o1R z;aa4q!0QyM#@+qMr&`GKk(XMTc)5139=HghQaC+xhW07n#}EzTkJ)5VAtScrlgKmt%Yw8y#Qy zun;F3Jo0pT7))eXcM>d4W)&HhX5_MkATdIX^j}(fHcyUw5l#rS?V;z5^&Uq{jcqDX zl-eL5NdG}auhA% zc~s2Yf>Ov9(y9H#^=&<&{3*{KO+0Q^v1?hhG`Bcpos~4K?(2&#NHrC*0S+v>DL2_h zR6NOfa$G&)P_MUS+YtGkI4^`^6KT%wJ z2n+o=g#@48X#( zDGm_B4cT}ySSoG`jO_#U3izE^T~h&>9D=@SVW;v6z+~h&IX%q7wR9l(#kR_wrVuR| zG?R3T9cKd+F0P3Txu_{AFIgktr%X>-v>|&sd3Ag)6c~|OLKJ&emJ5r_l2U{=9BX_I z6tvjuK*Ldbm1z^GN=g%KKKSu6CoPc4)0-wm7D-(f%M+}w9$0O}8I)oN+FNfo z{1@8B%5cO(LHx4#LrIuYP6leyrmhmEKD@faT;&*b%o~*kBvROk)LFe}<3N*}lk%k~ zpIwMcSkS!;2?j_oj{eQEaQ(9Nb09zfb+f+Ecyc@%&!=JZICs{1oKKcF%7ad;N~Mx= zYXz+6Hgu15YT+>>hKp|%3ip84FVhyW`W~gEgIAi~gk2NepUw#&pOs0-yY~X{ zE@xf8oE07z04J}c1keoV_}C%7AHQ(G`zO`e+q-seR=Z|j zTBSq7{Q7o+IzR5y>|2i9+X1f>Fm7%=ANNU(b^0eHT=};;$2;KVd5w4qVQ>`EoZWT% z2$)!CBBTsqnz2FpI$Dg&hRnDyXKP5fNqIjkTDajwPKO#Q0^)ERgAQ~F7A7x4kBZy% zNJnc)xG8%H6=%RB;Bisi2>f(4L{e-*oHMZ)YHy)(cr6ecV=PQhFv)$>YtXTZyKRY# znWlPXW%M6v@l9jGlyF2El~$q#$g~}-B4#v#+KyV;J85~GvE|~ZtSL=e1Q&1zEgP(Y z%B1XRa2AKEQ%a=d;af7vhU-Wu>0q%8BHUqar%h3Yp7qnib$UZ6*p3K6B&7f8!~Af20;#%mq8xE8Bjwsl+Mok1qBMte z8p7TL+u`Y_2a5SUP4UOf)#Gz*)7##Io5J(D?7N2fn||lK#1Na#xFIA%<1CDeV5pYc z$)H3QQWeOI+c^MW53l(WBq5(e;*LgH6>lV~7puQ0NIx~?IGMV?h0w06Rje>%x>Gt`2Ym-=uZR`-2i0a42`v7;|#B{sqAPiVr0@X>y_5PLh53x6wkz~zSnTj^(8bI z{=8Xg+&n9{Mhm`RG7EC{uSi@;SsDJ82GB0{KU$I%qD78oCDvh!<9k`fG%WKta%67T z3TD%iLy!kSVi9;S0fLzI`#m%fu zg&$kO3AqgD=zf(n4GNP7O!c*yk`HKm+pkh`OCd42gDHUs*AXQ%zV^A2voWG>5;EH| zei{QPuIsW!?w35L_d(zt5(CbwX@wj_qgRxn4vG%C!3?K&iQu6{2QvNycKw|XSTy_d zJq^fK=K|5cZFcK}e}@)GHUr&34cN!}WC&os`iyF0f;WgTAbLEL5_I#Z zw%N_BoY?{%C#p6;&FqU<}R)+ft=1mX98mZCAqokv$sc^sUT%(Bcv8n5ysD zTvs`W$}t{^eo7uF9<~=0Iwe_jV002OOH9o~V0AbeGep;76H}@#?RY1ZESWqIk`z|d z6X0rowRMwdvT{bu{sdtwqHf#$MDX(i9aeK*{qOpf|LhF@cVFQ@F*NuOc`pC2Cj~73 zFk0=OPx|k_>HlEAFfuXzkM>KCwuYV7D5@`d{*Qe?Z@h#H*T<9#E>NK`*qIgruupx^ zFMGpLek8U=Z4~nNU9Qt9*PG*LVI?8Z@Zn>})FyAUoVUk^z!;eCtagkw{m(vEq`_^v zH1wZO%h$sP!;g!H13NEXdtOGCZcjIlfiEZPy&$9_%xdTFUA4ZCu->oSru{E6zJoj7 z?&JIGwlwrQJ;v74Xr`79MLNt=+aSL^qArJTEz&P3*u2m*Q=%_m>r;HjeYaQN%Uz~d z>&L;n3;Z;6uD;$|TDos;N20<;MB3$YkaZ~AU({YK)pD6)YNriEZht)VSL;~-nFSW= zuS21KB|OyGA^R{o+$C(AdPX$!XELUoHSKqM-$2GSKlZ`o4MOk>@kPQBKi{&cadIz& zRTn+|yzxL6iBVxU_vlnD(^=K3{b=e{EPr`wetBZGY%a>VGIuh3sho~;Yly98TKBnD z{motO==^AIt@_yi_>*#%rB;=9vs24)+v=nqR@PBnXVm^sq{-0j`QiCz6W z5lr{C@3wthB06=I4Kj!_EX%v;ro7)VQ*?VRUya!8!ywVnb!qX9LBe6>NMqy_rVG&f z)5^7n0kM1^u@)$kNtkZP0(bX@RU5oJ4&$hA(~g}N*~Xu?uMzOrLDPp5N5w7jMDk3b z<+$*+vXM*A)P?h=%pY@yJ00480; zLb`kdSEq#&ng!4hnQ&@KVKzdZcGA$iBH>Ko7c1f<3Rxld1Jc=0=shL^aqbkRjEDzE zY!FnmbH5eXt)U^&QS4ytLTqfZTmB=zZTnOFmOY7z0d^KkHuF-kLz;M;!Oz=X&yDwL za3R1V>~y=1-ah}PhXL$+zP;hi-4)Y6>wO=XI{^aty6;4zIQG$S|*OW0 z>i(;WCK!t=KezMRR?H;PV&%(Vz$CAl8cP7lTrp@C(In zN8DGt23AAEDL~&JQ4p&f*dx*#^QBPXc7<(C8;N4dguY392?le1E)CQp##V=v(!`kV z74(BDngaC5_{kcM;Q;xD)7Iy}(8KJA1_ia(XF=R$Gb#j1&<2UY0XWb}#a)6~B&GIm zDnd^Q))A`%ufhS!O9jNK9Y56}*n^Y8mhp?pps?VAWi{}`rj~eOyW(3cZk#U;g{eE* z+X0-p5ALABvm8)&>6)WQ4>@7n77=ZlF>-9`D@Yi0m1oSgeA?ZcZI+YVD{XxE z-TxF{lwN0qu=ytMrxsuo7Yq*|NWBRz^*~xQ4B{VVYV%)*(X&m|#v(Qh-JA2D>N#c^ z;zY!O@j%W4TP_$0oVS*v4Q*BqZNh(&Y6e~!7Qqt@6=u%sg8aEkJH89>XMjzxkf=** zdd#r7enro^CZD2|q#Nr%8dj;Ah>vBUUUM4=-zCPsD~hBySVHrK6QCWMB33|HS2G?D z#YixPbcbv-i5C|Xu`==5feUS?O*35AyOl3ObhOfHr z>EfkD6znq{^U-s9oxmvhG)m`--aAM;1V?oLDlWI_asH?D6Gs_qHPh@&ty-nsdL}FvlOZO|KO@r6qgRJ!cEmS4_{Ni8oiB8V zUB2K(h+lP7_Whk$UIas9&Plv3s(@~@qm(SmQ>#Cmj*`)}JPJm$in;T$loqk{@M5U% zu{yBi7A>!^UxX8?4@6x$Q~X2Cs?y@c4}54Cf|r4jav}J4I^wDkTT%#0GNi^F!q)Nj zxF27nfb%{+RAsEI%v{9BM|^22k;l^R#TdO;%K_jIhazi1vM!)=#He1yx=%wKJd}&+ zjdq7f3oGX4#O%e_yw1@_njlX+>FJ0)>vJbXONj+W&_mtFq$)Imy)jS9!L?E3D+Gg` zvl=bwt1=ebuSS_g!wn*RI2rL+7kM;gmPR7jF1j zqI3(m`)$N!qddUXXSi=oW2n}$TBN3=ArSFx=$o&E%6eB{)0DXlcr)*m*D{6wcKE!& z3;Y>dN=L!IbiAEqsY%vR(aLgM$<4Cd@KsQV`dB@35k2IWPnWT;CaBM);q!JZN{#7x z8PzrrQyC1&iRrY*#(o7_ZBU~#Zzg?O1NZdrZ%AWmxWI^b{S)~fI14h5`qlnYqs zV@*|wKobsCMmp|eOC`e&_}$;g1}im*sE7JcR!%RlerM!E{F!11ks%n889uEp3J3+w z{M(j2*>mV<7i$4|9B2=sGOmlg{dbLGj`oVYC3`Sv`T`!O9SaW{c`QYD9_dakDbNnQ zy$wPAyD+vo9ciaR_=q;o@rD#B3aVxnC8=zIR@7a!9$GXDpwTh1`L;3}piA6HVQl41 zrU)wcGrEJK#_r_WZWcFHN{?EyEl<-?$3nMR#RpfFv9pKH`6h`DVF9g&{HAfkUd&*@ zi-1mqv$eU#%?|RtY14tHKY^G|m<@%mZBr_hvLp4SC(r0_8gFPG=z>0wh~!KF*G~R` z{umeI=D2zqyCzP?05fLqF{Y9={f#vQh;?|3=9MBF+X5G--$~FGCHFfYNJhUvKya?WHLOb9hW|H$8tg#>dl_Y zn(f383&#EZsK=Jr++O{1;;xCh0+X)qloKJ#}p^X0eqo!&T#3uTMSN=${wKv zaKLbBu%eir!`en5Z3#~*@bM_c+6e3FqXh0xd9FG5rla!y#Ob&4uO)0`QndRRQGee3YSKNC;hM z8-GIcRTOvOm>Y}t!+6d{1JB1cOtsDY#TPG%-^49puQ_p2tjbptJ{ zlM{5IL1An}TSs9F@{HVSQWrTiFg%&Qy@>Je;Nm$~p12*Kz*oY-bP}Ax^A{e3m{GtU zsdF5g){qxf4>M8KO(G6g4G8Q9NT}1XtjDa0x5C{$!;5%YP}F|@9*lhy%ZNlNrQ?j; z;zb6$hJy0i)DourKvu4GR_7D39Vaf%HBeTd!$u>@MhKR?8T!1v^I|5nHnCO|oe)l* z^@CDtx?OqV0yT6XnQZ{jGhj5frFjKGM@(uq-oRE^Gp$ZMZ!+M8q_)zd6PU?^Z<`l$ z`+DQj1~=cWiVW&hTu6d+f2}nqJd#6rLprY(zhY1lQ&D+@l6O`yy^|XL#@6SP%d?yM z)b=%e^BqS`No)84Z0b`>s%WgM<<#=BCYH6WH_^+?*=WbP(FbuOODVt2>wg*jo2|^#<>#B|*y4me1qrUg92| zgFGLHz&4Fbq|_NkEcHg2DyTT~1I~Cx6K9ntqgu&b9DpQP-KS|ld*yD@MTyV*^j(ps z!?|0aXq3l(zylF1j1wxF363~xVzZQlMBteYy=lqz{R43=&6aZwrA5`~!V(I(au!c> z#xSUHT>P?$9Kxw0`91%EYY}F7_4ml{AZZ^@(E64IwP0OVeu5ajWrc216F9KF4%_}9aXwRHZ5`-mVr)fw5#)qOetUZ_3GQvu#ex=P zN?IVy$3qoP2F1hMRd`41u+Y$ zt90p*P9pVa@?53RS#>dWC?BGk?i+$;(xbw7|roEBm z$()mG07u{C#Mf5VAmRM^)SYq^5A^$JW_oW4LT*4XFY*L!ex!o3Ls0(oi8|zZvb>>U zv4X7Z%w5I~4|*zJ!4)>`-XSxOVxcxx^bwc+AD0f{7;P5UK`eslYM@@DCsy(eT_w+h z_fZmCtePs_npScJ8$a?COB9NDVPuZ?O>-8k}Ycxn@?)?6%87UH#ytJ91jW*(zh#hgQ@Y;c}fQ#ijN zp6iWW;_WeOr?1O#9{aumPRCU0 zo=z5WtN*hh)j$|aME|B1b+B3C0`%KmB9mYPNc{ z7hWmaFqGn(a^-VzZc4c1YSW6pz&{+LLydFxSL#fJ!0(BK(74I|yqg$sYu~4`XM4Ul zw%{VmTDBb9(pu#>OpYm}ZxrjTDI#p!Yf?z{dUFRSe1tSX=0r>g$)<)&6eHhCRA~Cl z?CD;gJX;(%fh)?te3l{${uQBg-pU{)ysL{73Mh|9sN_x68xC#P&b! zB=%_A*ln>QecSf`2!vgRZI`ODdnYBoqTRih^!rfJwtpaCi_khO0N89bv59OBe!XPv z$D@>x7?BlN>-TIdLx(ZVPH{68KA*q*0L*uHad9JD&sC6q-)3#Pg@Nm=3f5)GuB)<& zrms|jq1f)~;^TFHJ>Tu|ITz;l0U8rwMg4RIy!>2lo(%2}JP+>=EJ7~=Gz+rP?cm`7 z;E`dOclNd<(#GCSN9o;3!ot0wkN~BYzkT2B9XBA198_)MSZu9qj~rlU+r*;uxj7H* zy*y*ToXGa44W+FE%q5_Ehc;w=r@I}tJeg@*0Y9YIktJT9EAc|VQYSn0vq5&Vbwdv; zo+10C57=D8u}n;h+j+oehcVe}Em$4$1=eHt3jri^p`3A@v8qi+_2J`X7v=i7w+k4L z{DKnR%<2$WgQ+t8$~gR_|1DNo2P7xE0)S_s>62G20K?F%lP4L7UQPimj$tn3ib;(k`jQ#%5gsSySE)PVef&c=zs0IT4OR|_ort_qj+4oO` zLKUzUdw4k>)+QcUey)&(y-^3fBf&8|VkIry~pQ z1l%YBXm=dkWZF$s~s*UOGW_ za4Zb%Qdp+=EEf0m!%|(jboJ_{FKP#J{G3=rJP&~0hvraGliCp9@u!pjj}5QcKC54)6!OR0ZPYOB9PC~t}lozH7WP1qACZ4C4_PCmiXD?mvY$J~)UYRdocS>2ReJem~UeQxCV6Xb-@kz~B#}HWDGebC-7D z-FbLlVz2|R6frNsAddO`9`4}GpH3KRnl$ejJ&4*l>T`t?&lT1k+6f^a{{Rm%2SM%i z1WP+)%1N5%@Nb9EO>IxPI|{E%TN)nfC*u^(mD`H0NKP_U4#1y@Mxne7n8Bu{B-AA0 zVidtRMyy$mzEBfsZq?|09_}WJIxlExWnOXMjiw9@Ynv}tO?EVjExTvN&;r@u@>PyT zJL3`V2d}RIALw9XbF{%=IPE*Rh7O}l;x|)leaP1(rr(ExL;h;}HebLp+MkI{SC5+4`y8w%QeE9<~X8q~gUY&OI$Vw4l<59Dz| zcy*GFDoZ4CqU`i_Fs$NWj2Yb2C}4WzE8x-+d4i+ii-+ z^d>wmi&K+?bjCV@vuxZK$GH4)tDqj1twA0eiY7+w3rgVX$oU);gXJaJ+7!x)$f}(=3x;vy@&W_Rt&{Lv3Dg@3B<%Rm?ex_#!t5PN zL!9>aLQZUg+0sxWbD9i99AszOLmd#i7x~aH`<(Lk`mq0|JI3lF4C(UDJ*&Put(M6W`Qy4JEpdd8J6E1ZT048&lqQ|yncPI>| z&Mk)>uJ?c2#s9e1PS0H!a?1(jvAvI{Klc2*m*f4ODo#T6gh3`tK|s9DGP#MLTeVl1 zc_|sK6%MbpbY_FWTuvq+sq`YR3@R$SHSNc!Jz1Dmg?u&YIvWNs(nB^fc*W6T8224n zl~6`~>EdyC#2w1_GRs$D^V~>$6Rx9o4yS$2j5aC_z+fjOmCig>P`=>^fX(|_E6YN} zG&+JDm>yNiCL${-Uzk`Ix5_H9e7wU#ie6OU>q?4e^_tDV9rNIS=}mZ!C5s-@t6ZX! z<n+~;SLYrO(w4$M-S|jErp*SobzJ(Pet55u=Ho zpV8gt%px|%6djLi;+!Wr8qvDJ2lYX;{Y5j7tRW3jDJsQEI(d!+GMJ9UP|jil1Ite1 zMDpoti(?=tOx!$Qz0K9Ic`|iXP8vCftt3x)!SdBP@s9<~{YS+i?)t}=-o%1sk?NYY zL1L{B#8#aq{f#r%p4wYOAM=Ho8UZ{F3it**Vt`({Vs}O-xR^+|PD)s?h>x5y%;%h& z0^yjbDev%8w60~`{`H!K3T@#D7G9ZegVXSY=6Wo%PQCRvUVtDKq`K{S?L~ks?R3bS zReQ!kDsS(%hm9Kk{bE+VeFF!a+m!8%Ynyiv=+wnBUAvu9Lc0Ezs*l82oqBprwS|}Z z#Y$A84^^Lhg_o6(Rb`V?ln~SbDnR(w9Bmne5yTEWiU=%7P>Jw7e|)byG zdiM#-p{YpNc;y`q49|spJVzZb2aHsxDuGkO zCEg#ErSL#ka*6%a@3d-Rw9k}YPnXM7(xGjZeOi=p6@)r{y2==|rnIXtZT~!x>IS`FGcuucFmu+ zl;7eKrS2S>{WMXIpI>f_3o1gSka3mHDvywz;gppGc8Dct(qxM3*2UWWgl`GiVOv|Q zXGH1v?&lkMt5z-V3B0$NFAhG~*urxsPHk_zrKo9+nQmwHx#IgQewnol(^#3$tquys z*w2nG9lm(0^}GU)eEZ&^KeVeX+ex`}=beS15Bl+jGP{46|2PLKmny1J{meJ(U^w^HeF6meh9K40QG)^+%Ql7?D*W=;}W@7%>@a=O54Sz%(z< zClI{hbSvoR{@*-*obFgm=)W7O9^~E)1wHpQT5Slw(Ir#642I7!-p^d)k2yfz9Qo$+D zR?k55$Z0!=duaE_%157JA@5z4d5i_gy4)Fds(wZsPl z;VA+6xH1~#MSwNj+-?~{p$gdemb#CB=b&KwW{{xa#KmI_%P)!Hfi~(T*4{Zq#-(69 z7HuygSgo;Y<0{w8Quv~%#J7Q+kyt?SWxlGfjK#J7)UV&59quD_14KKZkxO&g0Rl=r z0Qn~0IXtNPR)e|=7$|CdJ@fGGH%0qy3;Ad0Jyh-nQbsE)bd^)6;FaGU8iOVDrn_<# zPQ?{&{}#{Nc1%eDmMo)4On!>@^HhI$tdRN&4|XaWeMK$%6sOHD`cCppH0-3})nnkR zwE)=e0)K#Rn!)myZJ2bKfN4DY+U2Om1UKG9AEl^avf%1k(=j5Bet~-M8!x8f`S-Qx z|FSmqe_s9g&uP(rRiOX67xFIv!@pS&`G=~~e-26ghp5tjyQ=lSXAhWI{^!1x<-hC! z(J10Su?O_)-SMp~Hy?KsAi}s{4`{{#-07hNR>#x7!;mC2)*v5tm6lhh;&qm4mj-`9 zn{ymZIlHL&xIEt|sC~O_e{~%aS$W+ko0Xi4lx5t+T(t*Zerz5V z;q!6vzJvFU-Q}GY8s!B1)Wp5~ylcE%-p{{J-_NJ16&`k*ReC)h-Xn&cs*iGAdJ@$; zyk6~m0@1keKS-S+#_?SAa`3*p-urqGqW96tEgdv`n2aAEynii>;Il?eH-5Z4blptn z#m3PF%;o8=5WyWPn|6s+;BP0(n#RO8F^Hb&Ub_}~?YO&y#4GWrffOi>wDN$n7K9HoCl8E+S9u4Q5X+M+DB^B@WN3a3N}}NJX-^Kr-xVv7sT|wU(r} z190N6V;;)XZEJ@!>=c}|21>^$2EquO-Dww4C&KCjc@lkId?0+fJ?d;!5 z_@reIlxTPd#4%ryv^BDYUK~qE3AVZs6T}W~P&*{oVJ|$vFbG3-#d48(ap2!%i=uLY z+*~Bv-lSUIGx=ZM;LVZpLfOE84rW1(3{(XkiR#en&%SyGHUJekLJTZXY=6df_r77a z`$7}GfB!`iYH#t2dVxRsY-`>$3!uq57Zo2{E?F3CA@b7~5CpgUVa*}wgYpSVG{MtZ z%}Gyy(x1)M-~j76OYjw1Zg04jY;%o7fC~t>UhIW zcgO~}=S;KkfUF{QT*fInP}`YPvKg1_OROll;BC{3QQL7qFxvYxxKfUAhk!cJ$+3kEAxAV(osOq<)R4~sB$Gj00J4UK(uD1q3FQYEgpp8mt?Hp_4N&I*nmyZd8s?)bqY-;gk5D zcj(kN2t9yGvW?Fe^l8ks>>n>=UU_NrNB1dGDfwjH%fjf`cUOW>6l zzPP4~rdCQNo-kVFx;Z!AcWB@nUW|~qVXd97Ag0ZO)GMfsH?2bm;p-vwuwH@oEcbYQ z=hH91XHQlchHscm-)M|mvfpI=*|qTSCo1EolQ7LPM|VOUq<0f2M2@V5%l(ES9D+jLWh`RCvR$AT(dv@NI8{VtPd~{1HqL z58&7N`zF@`J(JnP+BJL;p)|P~`4SY6{iXwUNmWy^Cl@Yuq!I&M{&sX6KaMX{28Gyx z)L1~=A(;AoyFdfMUZ`cjMLPAlckqxUb9ekzujSJ+3i94~WYNE;5No9@&j~;wWGoW9 zTm-DxjH=6E$_#rjxrqg?jTS#8g`aKW|G1HN{Vg^|{VLxBujsSV!4xVX!B9^tSzr?7 zlI-woUhHNRm4BBL60A*QCJ5|OoWjT?udvK*Jx&+7H5VL8@5oy$f&NCsJGT(b>&Ms; zVD8LO=hvN=u=6yfio&X6KFl7>Q{TrD={8O~UmQY>`J~=5gri0E)~#ljn#2t8ryXzq zR@m2SK+I4TtzBecEgj7IZoYmO3wX;U34PgGXVR6L1wyYmhuxhV{vAw#PNX+Os=bte z?oM_;kIoyaQiRrbG1lawEk@ZG{WOsss;X=iD=1CP&ajmFm=G8MU5`=_ikzsve@I*q zdKm`#c(z2IY;SuYxEG96D;5{jsYmDq~*VIm(S>qn? z*!=bBVz%!*muUJB$!SBp;zzZB1*54shTBfOh|6j?s?nWeH+(-?e1AW_HnSW#2?$Xg zu5@+h3=OX#e>`1v=XJEAapclIGdn5Fj&%h~xv*ofq}rvLnTen$ z*bg*qp#Zn`6)Qff_r#YSr=R>5c!E47VJmuoc!h-j(-a{R}p6g0VOFk!oy)|#P zMc~d2^BR|nZp3WCeu!CWL$;~{{fH9;VJN>9XSw#- zvQH>H!}C1-cmXE*)>j}g`b;6^IEGQF%r?=wpJ#P_(hpSSTf-{=`sy6#!- zQg`tzJfT@ymRf{xfGl9SDJVyi@HcTlJ!*L~}Y*7Ch&DwC4!6FtsV_d_K`k9%BvBab3-|SJF zjhj>QxlG22c zG@{1sNx-!b%u9x-GKcZ-Z3XhAGgPE4^74va(?R1>&F-3KqA&-%jYOg{L^dm1B4R(4 zgl7brI3Oilz#Nzn;rPraE$ZOT%LUlR>co1UBQ4S}Ck=yH@DLwH7V^?dpdEJjshrWR zwKC)q`XgeH9-@}{9BMVhKM{Fkuo<7)2L9`~en`nXzfrLh8Qm97CSk=#$i9HIIaQ4O*;stQG&7+|B0MK3YzEmri2#&cUwJGj2&`~ZsX^hV%p_$> zjojW?XtvIBt+Fc#xsoBxJd^rN+~Z?8P2;*3p*Pgmo>J{jAjpNSrF>^5p5XHMej7t- zNC*%Tk@QVg@mW*$17pgl{F_zTor}l9%tm_!SU^0JV*_po`U@>Ee;H*+ci&LnQ66Bo z;Va{{%)jFdx&IW7P&3YCabrtL=Ex3&ev2L!VL&Ia*Fn4w&sQjC42-+vw(`b0FZN3F z#QotH2P2g_%TT0F!%lkG?f`X>k7UwGtB9dBQF^V7jZ&4wh9q-t7)lSzsM zs?*$?Cd`loe*Dh@hCvGVPqz)^&^lhfm~*)YB#4}h0vFkRPccX$k`xO~fb?Q*ndPr_ z(~|eMUM%qh`G^@shZ^FZ3OI;3=4f1!kyw<+P!jB-aacn%ad;s)D>TWOjmB(nfwZ!Z z&BphD0JI|aMdrlj+BPpR77EJZ_a%9U4JLfOJ55WIq>5vp!9Nf|4qr;%mxKr~BAb(# z(x~8f5t$RAa50jbMj_qQs?KKXv$nW&pQP#%bVf8>z^=-CoLP8E8K;+u>!gpO(y`Sz ztBD{V+{AvbAkjS=Iy=G;zBveIxXE11a<>qwYG zBCZ|VPpY|rk|`esk%4knrRUcikq+)tWL1B!qK}kQlCa?-qqpH$)@PqN=G<4++YN53 zLUv==+6#bV`AJC+_F|sg?sAY3v(> zvRDgee%o_*HBZ>v#E&PePl&>iiGk;t8#T+h8CjQ0Cr5(qT(-w%-5)VG56ls5g|;q{ zHKU>{>U&u);pGp=_9OWJT7W_vAghh6XnMk6=PMyJ4b0An{0ju)WchVN?c@(TvnbX2F(eiVzYoH01BPw8OoXwcRI|Cwcbt&4_uUtPPos!c*T1&JG8mN!^q}m;dU6yQz5Y zr5kN=-~01yGBq!6Zf^X5d2jenTvS+G8JQ-$GYa(daz9$)n7}>g@!i}0*ZFwZZeH#u zEK$)(o&tM(jkyp!@Zr1TJD>X# zGxtTH&`&5+;q7R&-Q3)6o$ppo$2Sn+(^w5Vwd7`vhwTIG$$8Y0$ue;FZ?*17 zH{b09B~zSo+l2BN3PUiSlPMsq7h`l2`Wb~+75wqa*{{I^(}?0ep-MlX8c)s7$j_$F zS72Rj@8Bx$Ws1v=@=&+4>PcmOd}0j zpKwp7rcc~E^Ej|?C^d!or3dUu#f2mx=j0#@#gdu)1bA;=uz_Ib9048;<6=E2m*%4a z{sB?5Wm{Dq8!R$lo7xsxR~h+;6T&Xjau>0B^O%I(o0^c$AIRdbZz6>4%JHtvJbzR_ zpKh_NW9el8wof2*8YR5_5&Zku+xz`ZH$S1Ev1bDaZv`Bkyqo#wQ$oAzZ?0tBc#jCi z9{@ML?fj|O$=tcLfe8Xp^QmS5=E_s%_oby6U%yKw{EBPe`K>KpULdh*0YPpPBUEPQ zA)n8MPLrduKbWaLakmLcIzC`Tp7M5CQ!F4ScT_~$DN_&NQ2g~MX4Fe`5P zm;F+0@VXJ6oc6;55UdpPCZWv6qFr>bb(x3XQ|8f2(GoX?(g%Q7tNBV%UF9sN&&WF; zF5SO zhN675DVCs-f8VPy^P~=AIzQ~e6qhU5Q8kM?3z!A|$)Do#`3wt#AGrnci;Fp@$DLa;H6-Ovo)g9U3woJ z@*R7NGf@1yjuC;9M)+e?m>4iPvY46tr)Oo4-vW4p=)+~eDk9e*Sj$7`0dU+-9AzO; z??m>c>fSm&#Mue78qrRJe~U?qy%t)9@L|0!+_7xnQ)pm!ds@`>f8CwEM<140d4u)Z zUE?Nq;5*DiHiDXIi&o`;)U(YhfR|J~7BbliKBRhzo8;W4!Upgdk>Jvdo^?Qp|3)3)I1C5)Se=g>o(9P2aXm~7VYn;E zGW>Gg+jBAGV9C9n;;|zv3Ge_>JsqxPs~Bhq&{PxWLG>T|60fp}mu&poBNB^t6IzqR zNj1$aUVj*9AN4nLOMv>AHmZ{*Q6iMVz$!x*E?ZpNJtFavDOJY)u)S^iNu(3JJ*%zf zZDTv-i5N22r-YXdS6>Pr=C$Fo z?<^hzuvu;<6FdA4nQyUdg9mXANyK3xOVTA1 zD(L3?59Nlw8IOqUg|W4Sw`A^VZaIDrrWx=ttRRl0od&KrOp%2W4uvR?{I8W_NiqI| zRKI$SU5)4deSBGo41uwg4g9?NvxxBbZ{$6vmQM?jFCsI8%={n#J5 zPmBj49LFH4e=uhg9qLmX2i6yucTT00qaujO8O=nlz;XvrI+qII49ne5aigI;)|#ws z4`7+KH)i5bLc!5?zut^c?tYb8kAJVBdfZrj3{bbPgY6fC(%?oy3ON=8mVEq}&x^2+ zS77^?PbHL+gpv%;ogKU|K|>2eb>HT!AA!0iVTKe(KHGa~sT0zz!A>l6^Si)GZ!-Li z`9ybQA(#hUC@{^;EwnK_D3jOwcQxWHQF|KDH{<8Ski?!Jkhu=MINUUy8tSBYlvP#X zoJ@nD=4b?Nu(wf-bc>3{vDqOc6;U6#YS@`eUrW$eVv+e&S(QS=#|6 z#^1pCig7_d{4#|E@bhK_M_;_-sqr6`r(eTS_qN-sWb#EcYZlciOp6TJWWRyp(R83s z&lw34)E9-r*IJQs$VuU7v8cao?XHNk4@TN{!3Tzb0{7M2J%B>3P zUtuOKS}(Xv*o20TJB{Pu5q_brGmS%%9=k)wwG;-IMlxXKO7m8|HOc0qiV8EQE|78b z8@X(DsJa>r%&Ysk{+%C0!Kj7Ub@Sk}Iu$?#!K6)ndl6LJ(#;3QKtWkk?~(EC584eT z=8a97gPU-TP3iTQ%P9afe-c_DLy`v&HJDMRrBRg4xkE|fq+o)O!u(gOT{4MvvPz!v z3we}*khRNo1J67sn|%PIyKo}NT?UlCjqCPS1&V$~H-m*UYxP zI5-5jm3ypNa|pYcg@T6&b+h#m%kRc~44|Yl@a{rOq{)!p8?9oTC~0v52|`QvGX-O2 zet0jjbd!W@k5bK0F@6>9-L7!Zr;R&|Bw`5yMmNw~OVggPIgS67n-k}xgtLQt|5o0^)x4xxXT!`6^Ih1^<9z-TA zj>9sQ!vU!eeeS>cS~>)^*=u!#$D)A_5w6SU)n#(z+}db7`2s+SGqv<}jGY^IRcne! zj?i4cRc?i3eyh0d4gKN?3}pUN^ko86qC=|}?@g6+OSR%kVB-yYLWmrtUyhPyZhkP{ zg7)3!NF?Se@sOAz=czT@Cw4)Conw{iy26O9cpomMmFxWlyqPXwvEo(uUx<6BAWeg? zO|#3@Wt&~LZSyPJwr$(CZQHhO+jduN@5IjR?Cc*gvm1ZJoMq-g#(VTe=Jh;z-L6j@ zN%VJAaS|+af?`j;G+4a_=tf(Z5x;SuW-S)DN}010Cof}`b!J4VUsqF?S-=Lz^8ns? z!X5;l!BhE1WFpa^Nxvz+7L>k09X5wO(tq7V4Ueb`=o zymz4~J^X9++kcV*n^k|3?3$M+3y714r7~B?RTtkDV`tYd3E(>)o&_JzG>HO-}@@`m(m(xa4rIM={eDl=g!7pj$$ z3zA$1U#=`r)TVgA^)Z>&oR*2N{Lw0UHiuifd!p(#e@QXb^Fd3Zr1v3U!nW_fY-nuJ#W)xGt4B-PV ziM>j8;vEWY65yaa_udlTye)`j2S1m@?jQ(LTzG7FgI!U(vmqT{VOV+Q#8s}to1FV!){=&9uK!S6Y?`@t-6eVB0Ko({{50Nx5;YLtso|jJQ%8PoHY~c1=_z zjG(AA#10^VPmOx1*yb#?PS{!LT0k}5wDZ6q1Qz_2P=hTdcu47>v2+hyCl&oBc3~HL zyx_^GRgbL~W}(ML?QOv5H=Hb!`IrY8JDATkiX?+3eoX89>dJ6SOLN z&99~Fw^ayXO_FJ$P-#iu@dMyMuBV)lIr-OZPiv+9j-fFq7qQ<^K-M`!u47!T9kbdd zpa-%t_K~Rj^2HotE!kz+NQ8;4FKp7txWd+uGnY%#oV>uMv-G63b49v=HpDgrKqHug ztQn<({^UE#j=<2>wr45wS+*nmAApcCMW@d~6vjJOg+9wH; z$H3}{0kH`p;~bIiiTX)wLfCM2s4{Hujkw|fvA`G}mH44~`blgd2`DHb+w><#{(g-{6*Abw0Tn)A`Yd8(PS0|Jk z7Bn8*`!|V{S~98&uEA%h;#XD@D!N2^Ip?LrrYjG{_i>(vFe9)qd=&~LT*WM{;uja#I656L)etuoFGVL;`Wj!>(yVOpM&MSJqq zG&BX#_xNM5)6>x3G@x_?vmjqvrnrMs>B#9dG^o9KdExO^)F&jDIlz8SFAMf$ep)w$ z)VpbAx?*(du6xMOZu4^{Q|56saep^n2N?%i5nOxWl0E|C<|$DpFx3N0Wn1a$(3A2eiF)%j~h9%hi- z+{ZzKJip0WsL3|j>*-o3(E}`Wj-mXHIGo2xeGLC-v2_rFbyU`$*1IHu= z)=PokNIP1nIMX5KcHrK6K}4Jwf&8nu%PLg1^1hLKyH!^=Wq^-$U2R%v{s$Uw9uNFK z1f8t({~gTxUjUu|_2tTc1DF3%+YmDW!~bRW{qJDr{~99w&+IN@{ZA9i{+FHp=Wkw^ zSy=yvB;sLhsYv4Xe@VnozGa6|`NrLfzl8);U6%uFM?iS1yy-`D=-u>JfAFa(P!z}& zE6U5GFSOIL&o8Qy7*S&Ba^5E&6Hyrdwo-b>LE!)NR(;%E_U7b%e}oRs&dQe7&dU0B zl?65kZVaya^6{0sWpkGWE)U?NAq#)atp1Et`n*LZgyw#u+q%E~_E9<>#m~w0`TBFb zE>1FgAIsO1MyEi5`cpJ3BbMpobf25rhVH0Dsr{`(TmQbgkW@K6Uy8pQbB?D`Om`G{ z5c{6%z;UpB%;hE9lX6>10_^qUzXA z`Bt@a;`H^of0>(;I}`8c>*MorEGl^!sNgB7xsOupTtYwhytx0U4PCx3crLDtz-yH{ zKAL(CMJ6t5>*RD4WX;Qz99A=R4>p4o^F)J7fS;e#I53m5rz6()Wf$p0s|)qI>nR2$Mh9_9w4 z9ShQIAx`I?UWZQMea@CdFvK|8`}uFJ!jp>_7HjilQFAC$`VpaA|5Sr3gb0rIgX{!4QWzGl6nDB zEX9tvi@zNiA>@Vpfu4e`t=?mR#hQq*7Bfd2Ntu~p+2?@|MB$aE7689T8mWz5UbC}3EQM)D5H!Jm%p4L@D|?98DAb_qg9et68sbYN5+>~v{Qx_2zo?~hB;sTNzyN5f-aP39M%4f zU=HRt#_cyNYRMb)&jGXjJ501Jn3E;1jdK$iPm>Agc74}>(tCvFH$^dknXFXR37cAY zs@QT8Q5^g`iKE8v&l;pffopdavWZ+*i~6Y*1K4p% zvMsmlZhHd~(<9CL5UQi4sG}vyfznul(iTBLZu!|<1;2G^owyx_5>>_9s)dUX>+R!f-llE9K7y=t|^>m)SBo^h>Dg@Y+HWOtA z+ER_Kd<~80nxtDIb(Ye6i87Ldl++3Qnr-Jy4)ge*uxiGiAUu}x8DFBh@(_Y{KxfpP zlN)mwKjlOPVASX{>=qL%QmgfS$@1`#wnxB2rX(T;et6t3Hd>#Pcr`K zWLB0RDVNEGH)P$Z#R|-5!N23^_z)aNRb(!_ED-{(eE}X331zr8Q-*0S%&L_sc3|A$ zK@4%>nzc(&Vm5Nbs>vonKu*Y{{hw4D_cUb zSE;$ulfl*(b79VLWWd=N$EDVghDm`2*HgO~2@r|V`YCcB9VwnFI&eB#`gk-*=h{mPoawu+UzrQyEDy8#wu}`Vv0C$QK|)OJn6|PEpoHbaYJI*a!W0Kg zDux!-TD8g49i|*gHezmmN@NE=5-~97wMSg+8+J74Vpjhw<(0E>E+G5!aUJ98IdV+$ z*8K{$aC04LaO9{UL~{IE*WHw=-MbU5wb2`%bjb5J}4LW*y?eP}&p~g;RUJdtZHk!{_K8er4|6Mp6{^=*Er9FcuF)tzc2P znfc^p7h0-Iu{-V*NY=}wnB`BXeck~UC>WHa3WOf#2MTPWJtQMddd~uBW$m~|JWz_7 z>g%xf1qR*fiK#nAHh6fToj2O~=L1*E*}PnLS}16c-h;4Gu|KTD;~Ru{pF464hpBMQ#EPS_0^4SW&8bkl2oL;cLgs=RVi zt@3KEnQxh1IcVPfg+dCy4D~pZoacWbZ1q?Vpfhnkh#@k~`S`pJ%1a$7w5iFC`Q%q< zt(v;PjhYXyS#;=W1`sayFoyIBSuCeFn4({pB})Sg6X~7jhZYk)wB8nv>p%oO$FO21 z&Pl3)1N+WdcaZ$o5pA~vT!+7{&t|MQq5z!E1D0u-y-uZ2^QIXpX30UQD^Wz^gF8VE z#stUylPPI-+&pdRXIMI)Cql#%r(^C1%Zut;gP#d)pGN(^Q(E%Sx z!I{-05<{e=5(;ME>L1vocz>Y6`dW&qoBLYGQ5wOz87q!580xPz-)Tv9;-8{TX_=eK z4L*q2VVokh6j40nbkz&##IIJWorMtlSjpu)Zsu4GM9TvhtUg4!t$X2RA1M=!DoLI6 z!6*InLoM$Ej#((9&V^W38``qvXKQ!E!mJ;khQlq|KLDc#YzNhHKHEl!BvzRgTPgaJ zD|fAvBRX^mDr%oAEWa2sZMr*-Jhn3vOpdSWAj_|vq^e9DSlVMvy3oi>Yjx7-)z9Yd zna>qAcaw6mG6ah;?B0HPln~@+FpYQN7mOv3o>NvsAC|C!ZTP?57v!T;b#?r~iprPA zHPSe`7b_2xNV$4NfW zV9Od#{VjxI3W%d7UFl%I8?jpb!DfaA|Knn45D`iZV4xEj>#PLSlXjCd&gnz8kd6)W< z%R~P*T(1qWr}0%7V;7Q$jHaSN^pzFuIJKgT1~;i^?xvh#NjA!tTbO#}WZ9=HNnM_F zEvhe@heVgiV2;-T3;%^|X}QcGrRC14&W>uRsI7Y@uJ)sS3>2I#pId}~bp*^Rla!u- zSiNo0XJccSa2eBDsts9?S=M*xyVp%+eK?Y!mvGMLD%;b@?}-beKnQoWihgl20Y{nc zDmYtL$uIW9pFko5g4i5m_LLAz?giW7_aGaEgNje65iv%miyShZh43h=%9Gq?PPGFn z-@ZGSim7oCoN)LB7W&4qw?dU8idO9Mbkb4Ho3>Mg;{PdWEQp(7lRHyph*Y=Ju#T@9 z;mTj#MSt%QM7B@_v*<*2&Te#2jY?runv_uTA)P1|!-p0*n9H~`b=V|Ld#gzfxusvo zl|qbSPBZKnZ*izYleH1Hgs6uJeXTzPfu^h`DX`ttPm=z@r33A{0VZ^uwR;6{>W=UT zHVW4(9J&t-+9gnbve4}Kk)VP|&ᒻIAgRmnI_LMy}&XIpDyj5p5*oijac)>G!E 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�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

LO20EG}$%xt2@bps#%3 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 Ombh`k2&KcE@L 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 Date: Sun, 14 Jan 2024 11:55:39 +0000 Subject: [PATCH 7/7] change some errors in markdown --- doc/Theory.html | 1387 ----------------------------------------------- doc/Theory.md | 1352 --------------------------------------------- doc/theory.md | 23 +- 3 files changed, 10 insertions(+), 2752 deletions(-) delete mode 100644 doc/Theory.html delete mode 100644 doc/Theory.md diff --git a/doc/Theory.html b/doc/Theory.html deleted file mode 100644 index f40dda1..0000000 --- a/doc/Theory.html +++ /dev/null @@ -1,1387 +0,0 @@ - - - - - Теория по алгоритмам - - - - - - - - - - -

Теория по алгоритмам

-

Содержание

-
-

1. Обходы графа

-

1.1. Ориентированный граф, псевдограф. Неориентированный граф, псевдограф. Связность в неориентированном графе, компоненты связности. Слабая и сильная связность в ориентированном графе. Компоненты слабой, сильной связности

-

Ориентированный граф

-
-

Ориентированным графом (англ. directed graph) GG называется пара G=(V,E)G = (V, E), где VV — множество вершин (англ. vertices), а EV×VE \subset V \times V — множество рёбер.

-
-
-

Псевдограф(мультиорграф) — это ориентированный граф, в котором разрешены кратные дуги, то есть дуги, имеющие те же начальные и конечные вершины.

-
-

Псевдографом GG называется упорядоченная пара G:=(V,A)G := (V, A), в которой VV — множество вершин, AA — мультимножество упорядоченных пар вершин. Элементы этого множества называются дугами.

-
-

Ориентированный граф называется слабо связным, если соответствующий неориентированный граф является связным;

-
-
-

Ориентированный граф называется сильно связным, если всякая вершина vv достижима из любой другой вершины vv;

-
-
-

Компонентой сильной связности ориентированного графа называется максимальный по включению сильно связный подграф. Другими словами, это подграф, любые две вершины которого принадлежат какому-либо циклу, и содержащий все такие циклы для своих вершин.

-
-
-

Компонентой слабой связности ориентированного графа называют его максимальный слабо связный подграф.

-
-

Неориентированный граф

-
-

Неориентированным графом (англ. undirected graph) GG называется пара G=(V,E)G = (V, E), где VV — множество вершин, а Ev,u:v,uVE \subset {{v,u} : v,u \in V} — множество рёбер.

-
-

Формально, псевдографом (мультифграфом) GG называется упорядоченная пара G:=(V,E)G := (V, E), в которой VV — множество вершин, EE — мультимножество неупорядоченных пар вершин. Элементы этого множества называются рёбрами.

-
-

Неориентированный граф называется связным, если все его вершины достижимы из некоторой вершины (эквивалентно, из любой его вершины).

-
-
-

Компонентой связности неориентированного графа называется максимальный по включению связный подграф

-
-

1.2. Обход в глубину. Цвета вершин. Времена входа и выхода. Лемма о белых путях (с доказательством)

-

Обход в глубину

-
-

Обход в глубину (поиск в глубину, англ. Depth-First Search, DFS) — один из основных методов обхода графа, часто используемый для проверки связности, поиска цикла и компонент сильной связности и для топологической сортировки.

-
-
#include <vector>
-
-std::vector<std::vector<int>> graph; // граф
-int vertex_count; // число вершин
-
-std::vector<int> color; // цвет вершины (0, 1, или 2)
-
-std::vector<int> time_in, time_out; // "времена" захода и выхода из вершины
-int dfs_timer = 0; // "таймер" для определения времён
-
-void dfs (int vertex) {
-  time_in[vertex] = dfs_timer++;
-  color[vertex] = 1;
-  for (int u : graph[vertex])
-    if (color[u] == 0)
-      dfs(u);
-  color[v] = 2;
-  time_out[v] = dfs_timer++;
-}
-
-

Лемма о белых путях

-
-

Лемма: Не существует такого момента выполнения поиска в глубину, в который бы существовало ребро из черной вершины в белую.

-
-

Доказательство: Пусть в процессе выполнения процедуры DFS нашлось ребро из черной вершины vv в белую вершину u. Рассмотрим момент времени, когда мы запустили DFS(v). В этот момент вершина vv была перекрашена из белого в серый, а вершина u была белая. Далее в ходе выполнения алгоритма будет запущен DFS(u), поскольку обход в глубину обязан посетить все белые вершины, в которые есть ребро из v. По алгоритму вершина vv будет покрашена в черный цвет тогда, когда завершится обход всех вершин, достижимых из нее по одному ребру, кроме тех, что были рассмотрены раньше нее. Таким образом, вершина vv может стать черной только тогда, когда DFS выйдет из вершины u, и она будет покрашена в черный цвет. Получаем противоречие.

-

1.3. Проверка связности неориентированного графа. Поиск цикла в неориентированном и ориентированном графе. Топологическая сортировка

-

Проверка связности неориентированного графа

-

Небольшая модификация алгоритма обхода в глубину, в которой будем возвращать количество посещенных вершин. Запустим такой DFS от некоторой вершины графа GG, если его результат равен V|V|, то мы побывали во всех вершинах графа, а следовательно он связен, иначе какие-то вершины остались непосещенными. Работает алгоритм за O(V+E)O(|V|+|E|)

-
std::vector<std::vector<int>> graph; // граф
-
-// visited — массив цветов вершин  
-int dfs(int vertex, std::vector<bool> visited):              
-  int visitedVertices = 1
-  visited[vertex] = true // помечаем вершину как пройденную
-  for (auto u : graph[vertex]) // проходим по смежным с u вершинам
-    // проверяем, не находились ли мы ранее в выбранной вершине
-    if (!visited[v])
-      visitedVertices += dfs(v, visited)
-    return visitedVertices
-
-

Поиск цикла в неориентированном и ориентированном графе

-

Условие:

-

Пусть дан ориентированный или неориентированный граф без петель и кратных рёбер. Требуется проверить, является ли он ациклическим, а если не является, то найти любой цикл.

-

Решение:

-

Произведём серию поисков в глубину в графе. Т.е. из каждой вершины, в которую мы ещё ни разу не приходили, запустим поиск в глубину, который при входе в вершину будет красить её в серый цвет, а при выходе - в чёрный. И если поиск в глубину пытается пойти в серую вершину, то это означает, что мы нашли цикл (если граф неориентированный, то случаи, когда поиск в глубину из какой-то вершины пытается пойти в предка, не считаются).

-
#include <iostream>
-#include <vector>
-
-int n;
-std::vector<vector<int>> graph;
-std::vector<int> color;
-std::vector<int> parent;
-int cycle_start, cycle_end;
-
-bool dfs(int vertex) {
-  color[vertex] = 1;
-  for (size_t i = 0; i < graph[vertex].size(); ++i) {
-    int $to$  = graph[vertex][i];
-    if (color[to] == 0) {
-      parent[to] = vertex;
-      if (dfs(to)) return true;
-    }
-    else if (color[to] == 1) {
-      cycle_end = vertex;
-      cycle_start = $to$ ;
-      return true;
-    }
-  }
-  color[vertex] = 2;
-  return false;
-}
-
-int main() {
-  // чтение графа
-
-  parent.assign(n, -1);
-  color.assign(n, 0);
-  cycle_start = -1;
-  for (int i = 0; i < n; ++i)
-    if (dfs(i))
-      break;
-
-  if (cycle_st == -1)
-    std::cout << "Acyclic" << "\n";
-  else {
-    std::cout << "Cyclic" << "\n";
-    std::vector<int> cycle;
-    cycle.push_back(cycle_start);
-    for (int vertex = cycle_end; vertex != cycle_start; vertex = parent[vertex])
-      cycle.push_back(vertex);
-    cycle.push_back(cycle_start);
-    reverse(cycle.begin(), cycle.end());
-    for (size_t i = 0; i < cycle.size(); ++i)
-      std::cout << cycle[i]+1 << " ";
-  }
-}
-
-

Топологическая сортировка

-

Условие: -Дан ориентированный граф с n вершинами и m рёбрами. Требуется перенумеровать его вершины таким образом, чтобы каждое рёбро вело из вершины с меньшим номером в вершину с большим. Иными словами, требуется найти перестановку вершин (топологический порядок), соответствующую порядку, задаваемому всеми рёбрами графа. -(Топологическая сортировка - упорядочивание вершин ациклического ориентированного графа согласно частичному порядку, заданному ребрами орграфа на множестве его вершин.) -Топологическая сортировка может быть не единственной (например, если граф — пустой; или если есть три такие вершины a, b, c, что из a есть пути в b и в c, но ни из b в c, ни из c в b добраться нельзя).

-

Топологической сортировки может не существовать вовсе — если граф содержит циклы (поскольку при этом возникает противоречие: есть путь и из одной вершины в другую, и наоборот).

-
#include <vector>
-
-int n; // число вершин
-std::vector<std::vector<int>> graph; // граф
-std::vector<bool> visited;
-std::vector<int> $to$ pological_sort;
-
-void dfs (int vertex) {
-  visited[vertex] = true;
-  for (int u : graph[vertex])
-    if (!visited[u] == 0)
-      dfs(u);
-  $to$ pological_sort.push_back(vertex);
-}
-
-void $to$ pological_sort() {
-  visited.assign(n, false);
-  answer.clear();
-  for (int i = 0; i < n; ++i)
-    if (!visited[i])
-      dfs(i);
-  reverse(answer.begin(), answer.end());
-}
-
-

1.4. Нахождение компонент сильной связности. Алгоритм Косарайю с доказательством корректности(концепция). Алгоритм Тарьяна без доказательства корректности

-

Нахождение компонент сильной связности

-

Условие:

-

Дан ориентированный граф GG, множество вершин которого VV и множество рёбер — EE. Петли и кратные рёбра допускаются. Обозначим через nn количество вершин графа, через mm — количество рёбер.

-

Решение:

-

Напомним, что компонентой сильной связности (strongly connected component) называется такое (максимальное по включению) подмножество вершин $C4, что любые две вершины этого подмножества достижимы друг из друга, т.е. для любых $u, vv \in C: u \rightarrow v, vv \rightarrow u$, где символом \rightarrow здесь и далее мы будем обозначать достижимость, т.е. существование пути из первой вершины во вторую. Понятно, что компоненты сильной связности для данного графа не пересекаются, т.е. фактически это разбиение всех вершин графа. Отсюда логично определение конденсации G как графа, получаемого из данного графа сжатием каждой компоненты сильной связности в одну вершину. Каждой вершине графа конденсации соответствует компонента сильной связности графа G, а ориентированное ребро между двумя вершинами CiC_i и CjC_j графа конденсации проводится, если найдётся пара вершин u из CiC_i, из CjC_j, между которыми существовало ребро в исходном графе, т.е. (u,v)(u, v) из EE.

-

Важнейшим свойством графа конденсации является то, что он ацикличен. Действительно, предположим, что CCC \rightarrow C', докажем, что CCC' \mapsto C. Из определения конденсации получаем, что найдутся две вершины uu из CC и vv из CC', что uvu \rightarrow v. Доказывать будем от противного, т.е. предположим, что CCC' \rightarrow C, тогда найдутся две вершины uu' из CC и vv' из CC', что vuv' \rightarrow u'. Но т.к. uu и uu' находятся в одной компоненте сильной связности, то между ними есть путь; аналогично для vv и vv'. В итоге, объединяя пути, получаем, что vuv \rightarrow u, и одновременно uvu \rightarrow v. Следовательно, uu и vv должны принадлежать одной компоненте сильной связности, т.е. получили противоречие, что и требовалось доказать

-

Алгоритм Косарайю с доказательством корректности(концепция)

-

Алгоритм Косарайю предназначен для поиска компонент сильной связности в ориентированном графе и состоит из трёх шагов:

-
    -
  1. -

    Выполнить поиск в глубину (DFS), пока не будут «помечены» все вершины. Вершина считается «помеченной», когда ей присвоено время выхода из рекурсии (см. основные понятия).

    -
  2. -
  3. -

    Инвертировать исходный граф

    -
  4. -
  5. -

    Выполнить DFS в порядке убывания пометок вершин.

    -
  6. -
-

Полученные деревья каждого такта DFS последнего шага являются компонентами сильной связности

-

Доказательство корректности алгоритма:

-

Немного уточним, что требуется доказать: Вершины uu и vv сильно связаны \Leftrightarrow после выполнения алгоритма они принадлежат одному дереву такта DFS.

-

Если вершины u и vv были сильно связаны в графе GG, на третьем этапе будет найден путь из одной вершины в другую (по Лемме (Инвертирование рёбер цикла не влияет на его цикличность)), так как на первом шаге был найден путь uvu \rightarrow v, а на третьем - путь vuv \rightarrow u. Это означает, что по окончании алгоритма обе вершины лежат в одном дереве.

-

Вершины uu и vv лежат в одном и том же дереве поиска в глубину на третьем шаге алгоритма. Значит, они обе достижимы из корня rr этого дерева.

-

Вершина rr была рассмотрена на 3 шаге раньше всех, значит время выхода из неё на 1 шаге больше, чем время выхода из вершин uu и vv. Из этого мы получаем 2 случая:

-

Обе эти вершины были достижимы из rr в исходном графе. Это означает сильную связность вершин uu и rr и сильную связность вершин vv и rr (по Лемме 3). Склеивая пути мы получаем связность вершин uu и vv (по Лемме 1)

-

Хотя бы одна вершина не достижима из rr в исходном графе, например vv. Значит и rr была не достижима из vv в исходном графе, так как время выхода из rr — больше (если бы она была достижима, то время выхода из vv было бы больше, чем из rr, просмотрите ещё раз первый шаг примера). Значит между этими вершинами нет пути (ни в исходном, ни в инвертированном графах), но последнего быть не может, потому что по условию vv достижима из r на 3 шаге (в инвертированном графе)

-

Значит, из случая 1 и не существования случая 2 получаем, что вершины uu и vv сильно связаны в обоих графах

-

Алгоритм Тарьяна без доказательства корректности

-

Основой для алгоритма является структура данных "Система непересекающихся множеств", которая и была изобретена Тарьяном (Tarjan).

-

Алгоритм фактически представляет собой обход в глубину из корня дерева, в процессе которого постепенно находятся ответы на запросы. А именно, ответ на запрос (v,u)(v, u) находится, когда обход в глубину находится в вершине uu, а вершина vv уже была посещена, или наоборот.

-

Итак, пусть обход в глубину находится в вершине vv (и уже были выполнены переходы в её сыновей), и оказалось, что для какого-то запроса (v,u)(v, u) вершина u уже была посещена обходом в глубину. Научимся тогда находить LCALCA этих двух вершин.

-

Заметим, что LCA(v,u)LCA(v, u) является либо самой вершиной vv, либо одним из её предков. Получается, нам надо найти самую нижнюю вершину среди предков vv (включая её саму), для которой вершина u является потомком. Заметим, что при фиксированном vv по такому признаку (т.е. какой наименьший предок vv является и предком какой-то вершины) вершины дерева дерева распадаются на совокупность непересекающихся классов. Для каждого предка pvp \neq v вершины vv её класс содержит саму эту вершину, а также все поддеревья с корнями в тех её сыновьях, которые лежат "слева" от пути до vv (т.е. которые были обработаны ранее, чем была достигнута vv).

-

Нам надо научиться эффективно поддерживать все эти классы, для чего мы и применим структуру данных "Система непересекающихся множеств". Каждому классу будет соответствовать в этой структуре множество, причём для представителя этого множества мы определим величину ANCESTOR — ту вершину pp, которая и образует этот класс.

-

Рассмотрим подробно реализацию обхода в глубину. Пусть мы стоим в некоторой вершине vv. Поместим её в отдельный класс в структуре непересекающихся множеств, ANCESTOR[v] = vv. Как обычно в обходе в глубину, перебираем все исходящие рёбра $(v, toto )$. Для каждого такого toto мы сначала должны вызвать обход в глубину из этой вершины, а потом добавить эту вершину со всем её поддеревом в класс вершины vv. Это реализуется операцией Union структуры данных "система непересекающихся множеств", с последующей установкой ANCESTOR = vv для представителя множества (т.к. после объединения представитель класса мог измениться). Наконец, после обработки всех рёбер мы перебираем все запросы вида (v,u)(v, u), и если u была помечена как посещённая обходом в глубину, то ответом на этот запрос будет вершина LCA(v,u)LCA(v,u) = ANCESTOR[FindSet(u)]. Нетрудно заметить, что для каждого запроса это условие (что одна вершина запроса является текущей, а другая была посещена ранее) выполнится ровно один раз.

-

Оценим асимптотику. Она складывается из нескольких частей. Во-первых, это асимптотика обхода в глубину, которая в данном случае составляет O(n)O(n). Во-вторых, это операции по объединению множеств, которые в сумме для всех разумных n затрачивают O(n)O(n) операций. В-третьих, это для каждого запроса проверка условия (два раза на запрос) и определение результата (один раз на запрос), каждое, опять же, для всех разумных n выполняется за O(1)O(1). Итоговая асимптотика получается O(n+m)O(n + m), что означает для достаточно больших mm (n=O(m))(n = O(m)) ответ за O(1)O(1) на один запрос

-

1.5. Компоненты реберной двусвязности. Мосты. Поиск мостов

-

Компоненты реберной двусвязности

-
-

Компонентами рёберной двусвязности (англ. costal doubly-linked components) графа называют его подграфы, множества вершин которых - классы эквивалентности рёберной двусвязности, а множества рёбер - множества ребер из соответствующих классов эквивалентности.

-
-

Мосты

-
-

Мостом называется такое ребро, удаление которого делает граф несвязным (или, точнее, увеличивает число компонент связности).

-
-

Поиск мостов

-
    -
  • -

    Алгоритм: -Запустим обход в глубину из произвольной вершины графа; обозначим её через root. Заметим следующий факт (который несложно доказать):

    -

    Пусть мы находимся в обходе в глубину, просматривая сейчас все рёбра из вершины vv. Тогда, если текущее ребро (v,to)(v,to) таково, что из вершины toto и из любого её потомка в дереве обхода в глубину нет обратного ребра в вершину vv или какого-либо её предка, то это ребро является мостом. В противном случае оно мостом не является. (В самом деле, мы этим условием проверяем, нет ли другого пути из vv в toto , кроме как спуск по ребру (v,to)(v,to) дерева обхода в глубину.) -Теперь осталось научиться проверять этот факт для каждой вершины эффективно. Для этого воспользуемся "временами входа в вершину", вычисляемыми алгоритмом поиска в глубину.

    -

    Итак, пусть tin[v]tin[v] — это время захода поиска в глубину в вершину vv. Теперь введём массив fup[v]fup[v], который и позволит нам отвечать на вышеописанные запросы. Время fup[v]fup[v] равно минимуму из времени захода в саму вершину tin[v]tin[v], времён захода в каждую вершину pp, являющуюся концом некоторого обратного ребра (v,p)(v, p), а также из всех значений fup[to]fup[to] для каждой вершины toto , являющейся непосредственным сыном vv в дереве поиска:

    -

    fup[v]=min(tin[v],tin[p],fup(to))fup[v] = \min(tin[v], tin[p], fup(to)) (v,p)(v, p) - back edge, (v,to)(v, to) - tree edge (здесь "back edge" — обратное ребро, "tree edge" — ребро дерева)

    -

    Тогда, из вершины vv или её потомка есть обратное ребро в её предка тогда и только тогда, когда найдётся такой сын toto , что fup[to]tin[v]fup[to] \leq tin[v]. (Если fup[to]=tin[v]fup[to] = tin[v], то это означает, что найдётся обратное ребро, приходящее точно в vv; если же fup[to]<tin[v]fup[to] < tin[v], то это означает наличие обратного ребра в какого-либо предка вершины vv.)

    -

    Таким образом, если для текущего ребра (v,to)(v,to) (принадлежащего дереву поиска) выполняется fup[to]>tin[v]fup[to] > tin[v], то это ребро является мостом; в противном случае оно мостом не является.

    -
  • -
-

1.6. Компоненты вершинной двусвязности. Точки сочленения. Поиск точек сочленения

-

Компоненты вершинной двусвязности

-
-

Компонентами вершинной двусвязности графа, называют его подграфы, множества ребер которых — классы эквивалентности вершинной двусвязности, а множества вершин — множества всевозможных концов ребер из соответствующих классов.

-
-

Точки сочленения

-
-

Точкой сочленения называется вершина, при удалении которой связный неориентированный граф становится несвязным.

-
-

Поиск точек сочленения

-
    -
  • -

    Алгоритм: -Запустим обход в глубину из произвольной вершины графа; обозначим её через rootroot. Заметим следующий факт (который несложно доказать):

    -

    Пусть мы находимся в обходе в глубину, просматривая сейчас все рёбра из вершины vrootv \neq root. Тогда, если текущее ребро (v,to)(v, to) таково, что из вершины toto и из любого её потомка в дереве обхода в глубину нет обратного ребра в какого-либо предка вершины v, то вершина vv является точкой сочленения. В противном случае, т.е. если обход в глубину просмотрел все рёбра из вершины vv, и не нашёл удовлетворяющего вышеописанным условиям ребра, то вершина vv не является точкой сочленения. (В самом деле, мы этим условием проверяем, нет ли другого пути из vv в toto ) -Рассмотрим теперь оставшийся случай: v=rootv = root. Тогда эта вершина является точкой сочленения тогда и только тогда, когда эта вершина имеет более одного сына в дереве обхода в глубину. (В самом деле, это означает, что, пройдя из rootroot по произвольному ребру, мы не смогли обойти весь граф, откуда сразу следует, что rootroot — точка сочленения). -(Ср. формулировку этого критерия с формулировкой критерия для алгоритма поиска мостов.)

    -

    Теперь осталось научиться проверять этот факт для каждой вершины эффективно. Для этого воспользуемся "временами входа в вершину", вычисляемыми алгоритмом поиска в глубину.

    -

    Итак, пусть tin[v]tin[v] — это время захода поиска в глубину в вершину v. Теперь введём массив fup[v]fup[v], который и позволит нам отвечать на вышеописанные запросы. Время fup[v]fup[v] равно минимуму из времени захода в саму вершину tin[v]tin[v], времён захода в каждую вершину p, являющуюся концом некоторого обратного ребра (v,p)(v, p), а также из всех значений fup[to]fup[to] для каждой вершины toto , являющейся непосредственным сыном vv в дереве поиска:

    -

    fup[v]=min(tin[v],tin[p],fup(to))fup[v] = \min(tin[v], tin[p], fup(to)) (v,p)(v, p) - back edge, (v,to)(v, to) - tree edge (здесь "back edge" — обратное ребро, "tree edge" — ребро дерева)

    -

    Тогда, из вершины vv или её потомка есть обратное ребро в её предка тогда и только тогда, когда найдётся такой сын toto , что fup[to]<tin[v]fup[to] < tin[v].

    -

    Таким образом, если для текущего ребра (v,to)(v,to) (принадлежащего дереву поиска) выполняется fup[to]tin[v]fup[to] \geq tin[v], то вершина vv является точкой сочленения. Для начальной вершины v=rootv = root критерий другой: для этой вершины надо посчитать число непосредственных сыновей в дереве обхода в глубину.

    -
  • -
-

1.7. Волновой алгоритм. Обход в ширину (применение очереди в волновом алгоритме)

-

Волновой алгоритм

-
-

Алгоритм волновой трассировки (волновой алгоритм) — алгоритм поиска пути, алгоритм поиска кратчайшего пути на планарном графе. Принадлежит к алгоритмам, основанным на методах поиска в ширину.

-
-

Алгоритм предназначен для поиска кратчайшего пути от стартовой ячейки к конечной ячейке, если это возможно, либо, при отсутствии пути, выдать сообщение о непроходимости.

-

Работа алгоритма включает в себя три этапа: инициализацию, распространение волны и восстановление пути.

-

Инициализация

-
Пометить стартовую ячейку 
-d := 0 
-
-

Распространение волны

-
ЦИКЛ
-  ДЛЯ каждой ячейки loc, помеченной числом d
-    пометить все соседние свободные непомеченные ячейки числом d + 1
-  КЦ
-  d := d + 1
-ПОКА (финишная ячейка не помечена) И (есть возможность распространения волны) 
-
-

Восстановление пути

-
ЕСЛИ финишная ячейка помечена
-ТО
-  перейти в финишную ячейку
-  ЦИКЛ
-    выбрать среди соседних ячейку, помеченную числом на 1 меньше числа в текущей ячейке
-    перейти в выбранную ячейку и добавить её к пути
-  ПОКА текущая ячейка — не стартовая
-  ВОЗВРАТ путь найден
-ИНАЧЕ
-  ВОЗВРАТ путь не найден
-
-

Обход в ширину (применение очереди в волновом алгоритме)

-
-

Поиск в ширину (англ. breadth-first search) — один из основных алгоритмов на графах, позволяющий находить все кратчайшие пути от заданной вершины и решать многие другие задачи.

-
-
    -
  • -

    Описание алгоритма -На вход алгоритма подаётся невзвешенный граф и номер стартовой вершины ss. Граф может быть как ориентированным, так и неориентированным — для алгоритма это не важно.

    -

    Основную идею алгоритма можно понимать как процесс «поджигания» графа: на нулевом шаге мы поджигаем вершину ss, а на каждом следующем шаге огонь с каждой уже горящей вершины перекидывается на всех её соседей, в конечном счете поджигая весь граф.

    -

    Если моделировать этот процесс, то за каждую итерацию алгоритма будет происходить расширение «кольца огня» в ширину на единицу. Номер шага, на котором вершина vv начинает гореть, в точности равен длине её минимального пути из вершины s.

    -
  • -
-
std::vector<int> graph;
-
-void bfs(int s) {
-    std::queue<int> queue;
-    queue.push(s);
-    
-    std::vector<int> depth(n, -1), parent(n);
-    depth[s] = 0;
-    
-    while (!queue.empty()) {
-        int vertex = queue.front();
-        queue.pop();
-        for (int u : g[vertex]) {
-            if (depth[u] == -1) {
-                queue.push(u);
-                depth[u] = depth[vertex] + 1;
-                parent[u] = vertex;
-            }
-        }
-    }
-} 
-
-

Теперь, чтобы восстановить кратчайший путь до какой-то вершины v, это можно сделать через массив p:

-
while (v != s) {
-    std::cout << v << endl;
-    v = p[v];
-}
-
-

Обратим внимание, что путь выведется в обратном порядке.

-

1.8. Критерий существования Эйлерова пути и цикла в ориентированном и неориентированном графе. Поиск эйлерова пути и цикла

-

Критерий существования Эйлерова пути и цикла в неориентированном графе

-
    -
  • -

    Согласно теореме, доказанной Эйлером, эйлеров цикл существует тогда и только тогда, когда

    -
      -
    1. Граф связный или будет являться связным, если удалить из него все изолированные вершины.
    2. -
    3. В нём отсутствуют вершины нечётной степени.
    4. -
    -
  • -
  • -

    Эйлеров путь в графе существует тогда и только тогда, когда

    -
      -
    1. Граф связный
    2. -
    3. Содержит не более двух вершин нечётной степени. Ввиду леммы о рукопожатиях, число вершин с нечётной степенью должно быть чётным. А значит эйлеров путь существует только тогда, когда это число равно нулю или двум. Причём, когда оно равно нулю, эйлеров путь вырождается в эйлеров цикл.
    4. -
    -
  • -
-

Критерий существования Эйлерова пути и цикла в ориентированном графе

-
    -
  • В ориентированном графе G=(V,E)G = (V, E) существует эйлеров цикл тогда и только тогда, когда: -
      -
    1. Входная степень любой вершины равна ее выходной степени.
    2. -
    3. Все компоненты слабой связности кроме, может быть одной, не содержат ребер.
    4. -
    -
  • -
  • В ориентированном графе G=(V,E)G = (V, E) существует эйлеров путь если: -
      -
    1. Входная степень любой вершины равна ее выходной степени, кроме двух вершин графа, для одной из которых (deg+) − (deg−) = 1, а для другой (deg+) − (deg−) = −1 .
    2. -
    3. Все компоненты слабой связности кроме, может быть одной, не содержат ребер.
    4. -
    -
  • -
-

Поиск эйлерова пути и цикла

-
    -
  • -

    Поиск эйлерова пути в графе -Можно всегда свести задачу поиска эйлерова пути к задаче поиска эйлерова цикла. Действительно, предположим, что эйлерова цикла не существует, а эйлеров путь существует. Тогда в графе будет ровно 2 вершины нечётной степени. Соединим эти вершины ребром, и получим граф, в котором все вершины чётной степени, и эйлеров цикл в нём существует. Найдём в этом графе эйлеров цикл (алгоритмом, описанным ниже), а затем удалим из ответа несуществующее ребро.

    -
  • -
  • -

    Поиск эйлерова цикла в графе -Будем рассматривать самый общий случай — случай ориентированного мультиграфа, возможно, с петлями. Также мы предполагаем, что эйлеров цикл в графе существует (и состоит хотя бы из одной вершины). Для поиска эйлерова цикла воспользуемся тем, что эйлеров цикл — это объединение всех простых циклов графа. Следовательно, наша задача — эффективно найти все циклы и эффективно объединить их в один.

    -

    Реализовать это можно, например, так, рекурсивно:

    -
    procedure find_all_cycles (v)
    -var массив cycles
    -1. пока есть цикл, проходящий через v, находим его
    -    добавляем все вершины найденного цикла в массив cycles (сохраняя порядок обхода)
    -    удаляем цикл из графа
    -2. идем по элементам массива cycles
    -    каждый элемент cycles[i] добавляем к ответу
    -    из каждого элемента рекурсивно вызываем себя: find_all_cycles (cycles[i])
    -
    -

    Достаточно вызвать эту процедуру из любой вершины графа, и она найдёт все циклы в графе, удалит их из графа и объединит их в один эйлеров цикл. -Для поиска цикла на шаге 1 используем поиск в глубину. -Сложность полученного алгоритма — O(E)O(|E|), то есть линейная относительно количества рёбер в данном графе

    -
  • -
-

2. Планарность графа

-

2.1. Формула Эйлера. Теорема Портнягина-Куратовского. Теорема Вагнера

-

Формула Эйлера

-

Для произвольного плоского связного графа GG с VV вершинами, EE ребрами и FF гранями справедливо следующее соотношение: VE+F=2V − E + F = 2.

-

Теорема Портнягина-Куратовского

-

Теорема Понтрягина — Куратовского, или теорема Куратовского, — теорема в теории графов, дающая необходимое и достаточное условие планарности графа. Имеет эквивалентную формулировку на языке миноров, так называемою теорему Вагнера.

-

Теорема утверждает, что графы K5K_5 (полный граф на 5 вершинах) и K3,3K_{3,3} (полный двудольный граф имеющий по 3 вершины в каждой доле) являются единственными минимальными непланарными графами.

-

Теорема Вагнера

-

Теорема Вагнера — характеризация планарных графов тесно связанная с теоремой Понтрягина — Куратовского.

-

Теорема утверждает, что конечный граф является планарным тогда и только тогда, когда его миноры не включают ни K5K_5 (полный граф с пятью вершинами), ни K3,3K_{3,3} (коммунальный граф, полный двудольный граф с тремя вершинами в каждой доле).

-

2.2. Гамма алгоритм. Контактная вершина

-

Гамма алгоритм

-
    -
  • Чтобы проверить планарность графа и произвести его плоскую укладку, удобно пользоваться гамма-алгоритмом.
  • -
  • Определения: -
      -
    1. Сегмент графа ГГ относительно подграфа HH — компонента связности графа GHG \setminus H.
    2. -
    3. Допустимая грань сегмента — грань графа HH, содержащая все контактные вершины.
    4. -
    5. Контактная вершина сегмента SS графа GG подграфа HH — любая вершина в SS и в HH.
    6. -
    7. Гамма-цепь сегмента — любая цепь без повторов вершин, содержащих ровно две контактные вершины — начало и конец.
    8. -
    -
  • -
  • Алгоритм: -
      -
    1. Уложить на плоскость любой цикл HH графа GG без повторов вершин.
    2. -
    3. Построить все сегменты S1S_1 , ...... , SkS_k графа GG по HH.
    4. -
    5. Если есть сегмент SiS_i c одной допустимой гранью — выбрать его.
    6. -
    7. Если все сегменты имеют несколько дополнительных граней — выбрать любой.
    8. -
    9. Выбрать произвольную гамма-цепь сегмента и уложить её в допустимую грань.
    10. -
    11. Перейти к шагу (2), добавив гамма-цепь к графу HH.
    12. -
    -
  • -
-

2.3. Теорема о корректности Гамма алгоритма. Асимптотика алгоритма

-

Теорема о корректности Гамма алгоритма

-
    -
  • -

    Теорема: Гамма-алгоритм корректен, то есть если GG — планарный граф, то результатом каждого шага гамма-алгоритма является частичная укладка GG'.

    -
  • -
  • -

    Доказательство: -Докажем индукцией по числу шагов.

    -

    База индукции: полученный на этапе инициализации граф G0G_0 является простым циклом, он будет присутствовать в любой укладке графа GG. Таким образом, G0G_0 является частичной укладкой.

    -

    Шаг индукции: пусть граф Gk1G_{k−1}, полученный на k1k−1-м шаге работы алгоритма, является частичной укладкой. Докажем, что граф Gk=Gk1LkG_k = G_{k−1} \cup L_k, полученный на kk-м шаге присоединением цепи LkL_k, также является частичной укладкой.

    -

    Заметим, что на текущем шаге нет такого сегмента SS относительно Gk1G_{k−1}, для которого бы выполнялось равенство Γ(S)=Γ(S) = \varnothing, так как в противном случае существовала бы цепь этого сегмента, контактные вершины которой принадлежали бы разным граням и укладка которой была бы невозможна. Следовательно, нельзя было бы уложить SS,что противоречит тому, что GG — планарный граф. Значит, мы можем рассматривать только следующие два случая:

    -
      -
    1. -

      Существует сегмент SS для которого есть единственная вмещающая грань ΓΓ, то есть Γ(S)=1|Γ(S)| = 1. Так как только грани ΓΓ принадлежат все контактные вершины SS, то укладка этого сегмента в эту грань неизбежна. Это значит, что помещая любую цепь LSL \subset S, снова получим частичную укладку графа.

      -
    2. -
    3. -

      Для любого сегмента SS Γ(S)2|Γ(S)| \geq 2. Построим граф A(Gk1)A(G'k−1), который по лемме 2 является двудольным. Рассмотрим его связную компоненту KK, которая содержит не менее двух вершин. Граф KK также является двудольным. По лемме 1 для любого сегмента SKS \in K справедливо Γ(S)=Γ1,Γ2Γ(S)={Γ1, Γ2}. Так как граф KK двудольный, то мы можем по очереди помещать сегменты KK в разные грани, причем конфликтующих сегментов не возникнет в силу четности всех циклов в графе. Результатом будет частичная укладка графа.

      -
    4. -
    -

    Таким образом, на каждом шаге мы получаем частичную укладку графа, что доказывает корректность гамма-алгоритма.

    -
  • -
-

Асимптотика алгоритма - O(n3)O(n^3).

-

3. Кратчайшие пути во взвешенном графе

-

3.1. Алгоритм Дейкстры. Доказательство корректности(с доказательством). Оценка времени работы. Дерево кратчайших путей

-

Алгоритм Дейкстры

-

Алгоритм Дейкстры (англ. Dijkstra’s algorithm) — алгоритм на графах, изобретённый нидерландским учёным Эдсгером Дейкстрой в 1959 году. Находит кратчайшие пути от одной из вершин графа до всех остальных. Алгоритм работает только для графов без рёбер отрицательного веса.

-
func dijkstra(s):
-    for $v \in V$
-        d[v] = $ \infinity $
-        used[v] = false
-    d[s] = 0
-    for $i \in V$
-        v = null
-        for $j \in V$    // найдём вершину с минимальным расстоянием
-            if !used[j] and (v == null or d[j] < d[v])
-                v = j
-        if d[v] == $\infinity$
-            break
-        used[v] = true
-        for e : исходящие из v рёбра     // произведём релаксацию по всем рёбрам, исходящим из v
-            if d[v] + e.len < d[e.to]
-                d[e.to] = d[v] + e.len
-
-

Доказательство корректности

-
    -
  • Пусть G=(V,E)G=(V,E) — ориентированный взвешенный граф, вес рёбер которого неотрицателен, s — стартовая вершина. Тогда после выполнения алгоритма Дейкстры d(u)=ρ(s,u)d(u)=\rho(s, u) для всех uu, где ρ(s,u)\rho (s,u) — длина кратчайшего пути из вершины ss в вершину uu.
  • -
-

Докажем по индукции, что в момент посещения любой вершины uu, d(u)=ρ(s,u)d(u)= \rho (s,u).

-

На первом шаге выбирается ss, для неё выполнено: d(s)=ρ(s,s)=0d(s) = \rho (s,s) = 0. Пусть для nn первых шагов алгоритм сработал верно и на n+1n+1 шагу выбрана вершина uu. Докажем, что в этот момент d(u)=ρ(s,u)d(u)= \rho (s,u). Для начала отметим, что для любой вершины vv, всегда выполняется d(v)ρ(s,v)d(v) \geq \rho (s,v) (алгоритм не может найти путь короче, чем кратчайший из всех существующих). Пусть PP — кратчайший путь из ss в uu, vv — первая непосещённая вершина на PP, zz — предшествующая ей (следовательно, посещённая). Поскольку путь PP кратчайший, его часть, ведущая из ss через zz в vv, тоже кратчайшая, следовательно ρ(s,v)=ρ(s,z)+w(zv)\rho(s,v)= \rho(s,z)+w(zv). По предположению индукции, в момент посещения вершины zz выполнялось d(z)=ρ(s,z)d(z)= \rho (s,z), следовательно, вершина vv тогда получила метку не больше чем d(z)+w(zv)=ρ(s,z)+w(zv)=ρ(s,v)d(z) + w(zv) = \rho (s,z) + w(zv) = \rho(s,v), следовательно, d(v)=ρ(s,v)d(v)=\rho(s,v). С другой стороны, поскольку сейчас мы выбрали вершину uu, её метка минимальна среди непосещённых, то есть d(u)d(v)=ρ(s,v)ρ(s,u)d(u) \leq d(v) = \rho (s,v) \leq \rho (s,u), где второе неравенсто верно из-за ранее упомянутого определения вершины vv в качестве первой непосещённой вершины на PP, то есть вес пути до промежуточной вершины не превосходит веса пути до конечной вершины вследствие неотрицательности весовой функции. Комбинируя это с d(u)ρ(s,u)d(u) \geq \rho(s,u), имеем d(u)=ρ(s,u)d(u)=\rho(s,u), что и требовалось доказать. Поскольку алгоритм заканчивает работу, когда все вершины посещены, в этот момент d(u)=ρ(s,u)d(u)= \rho (s,u) для всех uu.

-

Оценка времени работы

-
    -
  • Если m=n2m = n ^ 2(граф плотный), то O(n2)O(n^2).
  • -
  • Если m=nm = n(граф разреженный), то O(mlogn)O(m log n).
  • -
-

Дерево кратчайших путей

-

Решение ряда прикладных задач сводится к нахождению дерева кратчайших путей с корнем в заданной вершине ss. Такое дерево дает кратчайшие (s,v)(s, v)-пути от заданной вершины ss до любой вершины vv орграфа G=(V,Е)G = (V, Е). Если веса всех дуг орграфа GG неотрицательны, то дерево кратчайших путей можно построить с помощью алгоритма Дейкстры.

-

3.2. Потенциалы. Условие применимости алгоритма Дейкстры для измененных длин ребер. Потенциал π(v)=ρ(v,t)\pi(v) = \rho(v, t)

-

Потенциалы

-

Пусть дана транспортная сеть G(V,E)G(V,E), где VV — множество вершин графа, а EE — множество рёбер. Введем в каждой вершине потенциал π(v)\pi(v). Тогда потенциальный вес (то есть стоимость) ребра (u,v)(u,v) определяется как wp(u,v)=w(u,v)+π(u)π(v)w_p(u,v)=w(u,v)+\pi(u)−\pi(v)

-

Условие применимости алгоритма Дейкстры для измененных длин ребер

-

Можно применять, когда нет отрицательных циклов (следует из курса дискретной математики 1 курс 2 семестр)

-

Потенциал π(v)=ρ(v,t)\pi(v) = \rho(v, t)

-

3.3. Алгоритм A*. Условие монотонности на эвристику. Примеры эвристик

-

Алгоритм A*

-

Алгоритм A* - алгоритм поиска по первому наилучшему совпадению на графе, который находит маршрут с наименьшей стоимостью от одной вершины (начальной) к другой (целевой, конечной).

-

Порядок обхода вершин определяется эвристической функцией «расстояние + стоимость» (обычно обозначаемой как f(x)f(x)). Эта функция — сумма двух других: функции стоимости достижения рассматриваемой вершины (x) из начальной (обычно обозначается как g(x)g(x) и может быть как эвристической, так и нет), и функции эвристической оценки расстояния от рассматриваемой вершины к конечной (обозначается как h(x)h(x)).

-
    -
  1. Функция h(x)h(x) должна быть допустимой эвристической оценкой, то есть не должна переоценивать расстояния к целевой вершине. Например, для задачи маршрутизации h(x)h(x) может представлять собой расстояние до цели по прямой линии, так как это физически наименьшее возможное расстояние между двумя точками.
  2. -
  3. Более сильное условие — функция h(v)h(v) должна быть монотонной. (Эвристическая функция h(v)h(v) называетя монотонной (или преемственной), если для любой вершины v1v_1 и ее потомка v2v_2 разность h(v1)h(v_1) и h(v2)h(v_2) не превышает фактического веса ребра c(v1,v2)c(v_1, v_2) от v1v_1 до v2v_2, а эвристическая оценка целевого состояния равна нулю.)
  4. -
-
function A*(start, goal, f)
-     % множество уже пройденных вершин
-     var closed := the empty set
-     % множество частных решений
-     var open := make_queue(f)
-     enqueue(open, path(start))
-     while open is not empty
-         var p := remove_first(open)
-         var x := the last node of p
-         if x in closed
-            continue
-         if x = goal
-            return $\rho$
-         add(closed, x)
-         % добавляем смежные вершины
-         foreach y in successors(x)
-            enqueue(open, add_to_path(p, y))
-     return failure
-
-

Примеры эвристик

-
    -
  • Если мы можем перемещаться в четырех направлениях, то в качестве эвристики стоит выбрать манхэттенское расстояние h(v)=v.xgoal.x+v.ygoal.yh(v)=|v.x−goal.x|+|v.y−goal.y|.
  • -
  • Расстояние Чебышева применяется, когда к четырем направлениям добавляются диагонали: h(v)=max(v.xgoal.x,v.ygoal.y)h(v)=max(|v.x−goal.x|,|v.y−goal.y|).
  • -
  • Если передвижение не ограничено сеткой, то можно использовать евклидово расстояние по прямой: h(v)=((v.xgoal.x)2+(v.ygoal.y)2)1/2h(v)=((v.x−goal.x) ^ 2+(v.y−goal.y)^2) ^ {1/2}.
  • -
-

3.4. Алгоритм Форда-Беллмана. Хранение в матрице: DvkD_{vk} равно длине кратчайшего пути до вершины vv за ровно kk ребер (не более kk ребер). Доказательство корректности (полное). Оценка времени работы

-

Количество путей длины k рёбер можно найти с помощью метода динамического программирования. Пусть d[k][u]d[k][u] — количество путей длины k рёбер, заканчивающихся в вершине u. Тогда d[k][u]=Σv:vuE(d[k1][v])d[k][u]= \Sigma v: v u \in E(d[k−1][v]).

-

Аналогично посчитаем пути кратчайшей длины. Пусть ss — стартовая вершина. Тогда d[k][u]=minv:vuE(d[k1][v]+w(u,v))d[k][u]=\min v: v u \in E(d[k−1][v]+w(u,v)), при этом d[0][s]=0d[0][s]=0, а d[0][u]=+d[0][u]=+\infty

-

Если существует кратчайший путь от s до t, то ρ(s,t)=mind[k][t](k=0..n1)\rho(s,t)=\min d[k][t](k=0..n−1)

-

Пусть кратчайший путь состоит из kk ребер, тогда корректность формулы следует из динамики, приведенной ниже.

-
for k = 0 to |V|−2       // вершины нумеруются с единицы
-    for v∈V
-       for (u,v)∈E
-          d[k + 1][v] = min(d[k + 1][v], d[k][u] + ω(u,v))     // ω(u,v) — вес ребра uv
-
-
bool fordBellman(s):
-    for v∈V
-        d[v] = 1
-    d[s] = 0
-    for i = 0 to |V|−1
-        for (u,v)∈E
-            if d[v] > d[u] + ω(u,v)     // ω(u,v) — вес ребра uv
-                d[v] = d[u] + ω(u,v)
-    for (u,v)∈E
-        if d[v] > d[u] + ω(u,v)
-            return false
-    return true
-
-

Доказательство корректности(полное)

-

Во-первых, сразу заметим, что для недостижимых из vv вершин алгоритм отработает корректно: для них метка d[]d[] так и останется равной бесконечности (т.к. алгоритм Форда-Беллмана найдёт какие-то пути до всех достижимых из ss вершин, а релаксация во всех остальных вершинах не произойдёт ни разу).

-

Докажем теперь следующее утверждение: после выполнения ii фаз алгоритм Форда-Беллмана корректно находит все кратчайшие пути, длина которых (по числу рёбер) не превосходит ii.

-

Иными словами, для любой вершины aa обозначим через kk число рёбер в кратчайшем пути до неё (если таких путей несколько, можно взять любой). Тогда это утверждение говорит о том, что после kk фаз этот кратчайший путь будет найден гарантированно.

-

Доказательство.

-

Рассмотрим произвольную вершину a, до которой существует путь из стартовой вершины v, и рассмотрим кратчайший путь до неё: (p0=v,p1,...,pk=a)(p_0=v, p_1, ..., p_k=a). Перед первой фазой кратчайший путь до вершины p0=vp_0=v найден корректно. Во время первой фазы ребро (p0,p1)(p_0,p_1) было просмотрено алгоритмом Форда-Беллмана, следовательно, расстояние до вершины p1p_1 было корректно посчитано после первой фазы. Повторяя эти утверждения k раз, получаем, что после kk-й фазы расстояние до вершины pk=ap_k=a посчитано корректно, что и требовалось доказать.

-

Последнее, что надо заметить — это то, что любой кратчайший путь не может иметь более n1n-1 ребра. Следовательно, алгоритму достаточно произвести только n1n-1 фазу. После этого ни одна релаксация гарантированно не может завершиться улучшением расстояния до какой-то вершины.

-

Оценка времени работы

-

Инициализация занимает θ(V)\theta(V) времени, каждый из V1|V|−1 проходов требует θ(E)\theta(E) времени, обход по всем ребрам для проверки наличия отрицательного цикла занимает O(E)O(E) времени. Значит алгоритм Беллмана-Форда работает за O(VE)O(VE) времени.

-

3.5. Восстановление пути. Детектирование цикла отрицательного веса. Поиск самого цикла

-

Восстановление пути

-
void solve() {
-  vector<int> d (n, INF);
-  d[v] = 0;
-  vector<int> p (n, -1);
-  for (;;) {
-    bool any = false;
-    for (int j=0; j<m; ++j)
-      if (d[e[j].a] < INF)
-        if (d[e[j].b] > d[e[j].a] + e[j].cost) {
-          d[e[j].b] = d[e[j].a] + e[j].cost;
-          p[e[j].b] = e[j].a;
-          any = true;
-        }
-    if (!any)  break;
-  }
- 
-  if (d[t] == INF)
-    cout << "No path from " << v << " to " << t << ".";
-  else {
-    vector<int> path;
-    for (int cur=t; cur!=-1; cur=p[cur])
-      path.push_back (cur);
-    reverse (path.begin(), path.end());
- 
-    cout << "Path from " << v << " to " << t << ": ";
-    for (size_t i=0; i<path.size(); ++i)
-      cout << path[i] << ' ';
-  }
-}
-
-

Здесь мы сначала проходимся по предкам, начиная с вершины t, и сохраняем весь пройденный путь в списке path\rm path. В этом списке получается кратчайший путь от v до t, но в обратном порядке, поэтому мы вызываем reverse\rm reverse от него и затем выводим.

-

Детектирование цикла отрицательного веса

-

Пусть G=(V,E)G=(V,E) — взвешенный ориентированный граф, ss — стартовая вершина. Если граф GG не содержит отрицательных циклов, достижимых из вершины ss, то алгоритм возвращает true\rm true и для всех vVv \in V d[v]=δ(s,v)d[v]= \delta (s,v). Если граф GG содержит отрицательные циклы, достижимые из вершины ss, то алгоритм возвращает false\rm false.

-

Поиск самого цикла

-

Приведенная выше реализация позволяет определить наличие в графе цикла отрицательного веса. Чтобы найти сам цикл, достаточно хранить вершины, из которых производится релаксация.

-

Если после V1|V|−1 итерации найдется вершина vv, расстояние до которой можно уменьшить, то эта вершина либо лежит на каком-нибудь цикле отрицательного веса, либо достижима из него. Чтобы найти вершину, которая лежит на цикле, можно V1|V|−1 раз пройти назад по предкам из вершины vv. Так как наибольшая длина пути в графе из V|V| вершин равна V1|V|−1, то полученная вершина u будет гарантированно лежать на отрицательном цикле.

-

Зная, что вершина u лежит на цикле отрицательного веса, можно восстанавливать путь по сохраненным вершинам до тех пор, пока не встретится та же вершина u. Это обязательно произойдет, так как в цикле отрицательного веса релаксации происходят по кругу.

-

3.6. Нахождение кратчайших путей с учетом циклов отрицательного веса

-

Заметим, что updated_vertices на V|V|-м шаге содержит два типа вершин: те, которые лежат на цикле отрицательного веса и те, которые сами достижимы из какого-то цикла отрицательного веса. При этом из доказательства леммы выше мы знаем, что от каждого (!) цикла отрицательного веса в множестве будет хотя бы по одному представителю. Из этих двух наблюдений следует корректность и полнота следующего алгоритма:

-

Запускаем Форда-Беллмана на V|V| шагов. Если на последнем шаге до вершины расстояние не поменялось, то оно и кратчайшее. Если поменялось, то до неё и до всех вершин, достижимых из неё, расстояние от стартовой вершины равно минус бесконечности. Чтобы найти все вершины с расстоянием минус бесконечность, достаточно запустить dfs из всех вершин, расстояние до которых уменьшилось на последней итерации алгоритма.

-

3.7. Алгоритм Флойда. Доказательство (концепция). Восстановление пути

-

Алгоритм Флойда

-
-

Алгоритм Флойда (алгоритм Флойда–Уоршелла) — алгоритм нахождения длин кратчайших путей между всеми парами вершин во взвешенном ориентированном графе. Работает корректно, если в графе нет циклов отрицательной величины, а в случае, когда такой цикл есть, позволяет найти хотя бы один такой цикл.

-
-

Ключевая идея алгоритма — разбиение процесса поиска кратчайших путей на фазы.

-

Перед kk-ой фазой (k=1...n)(k = 1 ... n) считается, что в матрице расстояний d[][]d[][] сохранены длины таких кратчайших путей, которые содержат в качестве внутренних вершин только вершины из множества 1,2,...,k1{1, 2, ..., k-1} (вершины графа мы нумеруем, начиная с единицы).

-

Иными словами, перед kk-ой фазой величина d[i][j]d[i][j] равна длине кратчайшего пути из вершины ii в вершину jj, если этому пути разрешается заходить только в вершины с номерами, меньшими k (начало и конец пути не считаются).

-

Легко убедиться, что чтобы это свойство выполнилось для первой фазы, достаточно в матрицу расстояний d[][]d[][] записать матрицу смежности графа: d[i][j]=g[i][j]d[i][j] = g[i][j] — стоимости ребра из вершины ii в вершину jj. При этом, если между какими-то вершинами ребра нет, то записать следует величину "бесконечность". Из вершины в саму себя всегда следует записывать величину 0, это критично для алгоритма.

-

Пусть теперь мы находимся на kk-ой фазе, и хотим пересчитать матрицу d[][]d[][] таким образом, чтобы она соответствовала требованиям уже для k+1k+1-ой фазы. Зафиксируем какие-то вершины ii и jj. У нас возникает два принципиально разных случая:

-

Кратчайший путь из вершины ii в вершину jj, которому разрешено дополнительно проходить через вершины 1,2,...,k{1, 2, ..., k}, совпадает с кратчайшим путём, которому разрешено проходить через вершины множества 1,2,...,k1{1, 2, ..., k-1}. -В этом случае величина d[i][j]d[i][j] не изменится при переходе с kk-ой на k+1k+1-ую фазу.

-

"Новый" кратчайший путь стал лучше "старого" пути. Это означает, что "новый" кратчайший путь проходит через вершину kk. Сразу отметим, что мы не потеряем общности, рассматривая далее только простые пути (т.е. пути, не проходящие по какой-то вершине дважды).

-

Тогда заметим, что если мы разобьём этот "новый" путь вершиной kk на две половинки (одна идущая iki \rightarrow k, а другая — kjk \rightarrow j), то каждая из этих половинок уже не заходит в вершину kk. Но тогда получается, что длина каждой из этих половинок была посчитана ещё на k1k-1-ой фазе или ещё раньше, и нам достаточно взять просто сумму d[i][k]+d[k][j]d[i][k] + d[k][j], она и даст длину "нового" кратчайшего пути.

-

Объединяя эти два случая, получаем, что на kk-ой фазе требуется пересчитать длины кратчайших путей между всеми парами вершин ii и jj следующим образом:

-
new_d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
-
-

Таким образом, вся работа, которую требуется произвести на kk-ой фазе — это перебрать все пары вершин и пересчитать длину кратчайшего пути между ними. В результате после выполнения nn-ой фазы в матрице расстояний d[i][j]d[i][j] будет записана длина кратчайшего пути между ii и jj, либо бесконечность, если пути между этими вершинами не существует.

-

Последнее замечание, которое следует сделать, — то, что можно не создавать отдельную матрицу newd[][]new_d[][] для временной матрицы кратчайших путей на kk-ой фазе: все изменения можно делать сразу в матрице d[][]d[][]. В самом деле, если мы улучшили (уменьшили) какое-то значение в матрице расстояний, мы не могли ухудшить тем самым длину кратчайшего пути для каких-то других пар вершин, обработанных позднее.

-

Доказательство (концепция)

-

Доказательство "правильности" работы этого алгоритма также очевидно и выполняется с помощью математической индукции по kk, показывая, что на kk-й итерации вершина kk включается в путь только тогда, когда новый путь короче старого.

-

Восстановление пути

-

Легко поддерживать дополнительную информацию — так называемых "предков", по которым можно будет восстанавливать сам кратчайший путь между любыми двумя заданными вершинами в виде последовательности вершин.

-

Для этого достаточно кроме матрицы расстояний d[][]d[][] поддерживать также матрицу предков p[][]p[][], которая для каждой пары вершин будет содержать номер фазы, на которой было получено кратчайшее расстояние между ними. Понятно, что этот номер фазы является не чем иным, как "средней" вершиной искомого кратчайшего пути, и теперь нам просто надо найти кратчайший путь между вершинами ii и p[i][j]p[i][j], а также между p[i][j]p[i][j] и jj. Отсюда получается простой рекурсивный алгоритм восстановления кратчайшего пути.

-

3.8. Нахождение цикла отрицательного веса

-

Если в графе есть циклы отрицательного веса, то формально алгоритм Флойда-Уоршелла неприменим к такому графу.

-

На самом же деле, для тех пар вершин ii и jj, между которыми нельзя зайти в цикл отрицательного вес, алгоритм отработает корректно.

-

Для тех же пар вершин, ответа для которых не существует (по причине наличия отрицательного цикла на пути между ними), алгоритм Флойда найдёт в качестве ответа какое-то число (возможно, сильно отрицательное, но не обязательно). Тем не менее, можно улучшить алгоритм Флойда, чтобы он аккуратно обрабатывал такие пары вершин и выводил для них, например, - бесконечность.

-

Для этого можно сделать, например, следующий критерий "не существования пути". Итак, пусть на данном графе отработал обычный алгоритм Флойда. Тогда между вершинами ii и jj не существует кратчайшего пути тогда и только тогда, когда найдётся такая вершина tt, достижимая из ii и из которой достижима jj, для которой выполняется d[t][t]<0d[t][t] < 0.

-

Кроме того, при использовании алгоритма Флойда для графов с отрицательными циклами следует помнить, что возникающие в процессе работы расстояния могут сильно уходить в минус, экспоненциально с каждой фазой. Поэтому следует принять меры против целочисленного переполнения, ограничив все расстояния снизу какой-нибудь величиной (например, - Бесконечность).

-

3.9. Алгоритм Джонсона. Добавление фиктивного корня и фиктивных ребер для запуска алгоритма Форда-Беллмана

-

Алгоритм Джонсона

-
-

Алгоритм Джонсона позволяет найти кратчайшие пути между всеми парами вершин в течение времени O(V2log(V)+VE)O(V \cdot 2 \log(V) + VE). Для разреженных графов этот алгоритм ведет себя асимптотически быстрее алгоритма Флойда. Этот алгоритм либо возвращает матрицу кратчайших расстояний между всеми парами вершин, либо сообщение о том, что в графе существует цикл отрицательной длины.

-
-

В этом алгоритме используется метод изменения веса (англ. reweighting). Суть его заключается в том, что для заданного графа G строится новая весовая функция ωφ\omega_\varphi, неотрицательная для всех ребер графа G и сохраняющая кратчайшие пути. Такая весовая функция строится с помощью так называемой потенциальной функции.

-

Пусть φ:VR\varphi: V \rightarrow R — произвольное отображение из множества вершин в вещественные числа. Тогда новой весовой функцией будет ωφ(u,v)=φ(u,v)+φ(u)φ(v)\omega_\varphi(u,v)=\varphi(u,v)+\varphi(u)−\varphi(v).

-

Такая потенциальная функция строится добавлем фиктивной вершины ss в GG, из которой проведены ориентированные ребра нулевого веса во все остальные вершины графа, и запуском алгоритма Форда-Беллмана из нее (φ(v)\varphi(v) будет равно длине кратчайшего пути из ss в vv). На этом же этапе мы сможем обнаружить наличие отрицательного цикла в графе.

-

Теперь, когда мы знаем, что веса всех ребер неотрицательны, и кратчайшие пути сохранятся, можно запустить алгоритм Дейкстры из каждой вершины и таким образом найти кратчайшие расстояния между всеми парами вершин.

-

Добавление фиктивного корня и фиктивных ребер для запуска алгоритма Форда-Беллмана

-

Как в случае применения алгоритма Беллмана-Форда для обнаружения отрицательных циклов, существуют два способа сделать вес каждого ребра неотрицательным в произвольной сети без отрицательных циклов. Либо можно начать с истока в каждом сильно связном компоненте, либо добавить фиктивную вершину с ребром нулевой длины в каждую вершину сети. В любом случае получится остовный лес кратчайших путей, которым можно воспользоваться для присвоения весов вершинам (вес пути из корня в данную вершину в ее SPT).

-

4. Остовные деревья

-

4.1. Остовное дерево. Построение с помощью обхода в глубину и в ширину

-

Остовное дерево

-
-

Остовное дерево графа (англ. Spanning tree) — это дерево, подграф данного графа, с тем же числом вершин, что и у исходного графа. Неформально говоря, остовное дерево получается из исходного графа удалением максимального числа рёбер, входящих в циклы, но без нарушения связности графа. Остовное дерево включает в себя все n вершин исходного графа и содержит n1n-1 ребро.

-
-

Построение с помощью обхода в глубину и в ширину

-

Процедуры обхода графа в глубину и в ширину можно простым способом использовать для нахождения остовов. В обоих случаях достижение новой вершины графа uu из вершины vv вызывает "включение" в остовное дерево ребра (u,v)(u, v).

-
void Spisok::Depth_First_Search (Lref r)
-//Рекурсивный обход графа в глубину. r - указатель 
-//на структуру Вирта.
-{
-  Tref t;
-
-  t = (*r).Trail; cout<<(*r).Key; (*r).Flag = FALSE;
-  while (t!=NULL)
-  { if ((*(*t).Id).Flag) Depth_First_Search ((*t).Id); t = (*t).Next; }
-} 
-
-
void Spisok::Ostov_Depth (Lref r)
-//Рекурсивный обход графа в глубину, соединенный с
-//нахождением ребра остовного дерева.
-//r - указатель на структуру Вирта.
-{
-  Tref t;
-  Lref s;
-
-  s = r;
-  t = (*r).Trail; (*r).Flag = FALSE;
-  while ( t != NULL )
-  { if ((*(*t).Id).Flag) 
-    { cout << "(" << s->Key << "," << t->Id->Key << ") ";
-      Ostov_Depth ((*t).Id);
-    }
-    t = (*t).Next; 
-  }
-}
-
-

4.2. Определение минимального остовного дерева

-
-

Минимальное остовное дерево (англ. minimum spanning tree) графа — это его ациклический связный подграф, в который входят все его вершины, обладающий минимальным суммарным весом ребер. Заметим, что граф может содержать несколько минимальных остовных деревьев.

-
-

4.3. Теорема о разрезе. Доказательство

-

Теорема о разрезе

-

Теорема Форда — Фалкерсо́на — теорема о максимальном потоке в графе, тесно связанная с теоремой Менгера. -Звучит так: величина максимального потока в графе путей равна величине пропускной способности его минимального разреза.

-

Доказательство:

-

Достаточность: любой поток между вершинами t и s меньше или равен величине любого сечения. Пусть дан некоторый поток и некоторое сечение. Величина данного потока складывается из величин «грузов», перевозимых по всем возможным путям из вершины t в s. Каждый такой путь обязан иметь общее ребро с данным сечением. Так как по каждому ребру сечения суммарно нельзя перевести «груза» больше, чем его пропускная способность, поэтому сумма всех грузов меньше или равна сумме всех пропускных способностей рёбер данного сечения. Утверждение доказано.

-

Отсюда следует, что любой поток меньше или равен величине минимального сечения, а значит и максимальный поток меньше или равен величине минимального сечения.

-

4.4. Алгоритм Прима. Аналогия с алгоритмом Дейкстры. Оценка времени работы для различных реализаций очереди с приоритетом: бинарная куча, Фибоначчиева куча (последнее без доказательства)

-

Алгоритм Прима

-
-

Алгоритм Прима (англ. Prim's algorithm) — алгоритм поиска минимального остовного дерева (англ. minimum spanning tree, MST) во взвешенном неориентированном связном графе.

-
-

Аналогия с алгоритмом Дейкстры

-

Данный алгоритм очень похож на алгоритм Дейкстры. Будем последовательно строить поддерево FF ответа графе GG, поддерживая приоритетную очередь QQ из вершин GFG \setminus F, в которой ключом для вершины vv является min(ω(uv)(uV(F),(u,v)E(G)))\min(\omega(uv) (u \in V(F), (u, v) \in E(G))) — вес минимального ребра из вершин FF в вершины GFG \setminus F. Также для каждой вершины в очереди будем хранить p(v)p(v) — вершину uu, на которой достигается минимум в определении ключа. Дерево FF поддерживается неявно, и его ребра — это пары (v,p(v))(v,p(v)), где vGrQv \in G \setminus {r} \setminus Q, а rr — корень FF. Изначально FF пусто и значения ключей у всех вершин равны ++\infty. Выберём произвольную вершину rr и присвоим её ключу значение 0. На каждом шаге будем извлекать минимальную вершину vv из приоритетной очереди и релаксировать все ребра (v,u)(v, u), такие что uQu \in Q, выполняя при этом операцию decreaseKey\rm decreaseKey над очередью и обновление p(v)p(v). Ребро (v,p(v))(v,p(v)) при этом добавляется к ответу.

-

Оценка времени работы для различных реализаций очереди с приоритетом: бинарная куча, Фибоначчиева куча

-

Двоичная куча - O(ElogV)O(ElogV) -(Чтобы упростить операцию decreaseKey\rm decreaseKey можно написать кучу на основе сбалансированного бинарного дерева поиска. Тогда просто удалим вершину и добавим ее обратно уже с новым ключом. Асимптотика таких преобразований O(logn)O(logn). Если же делать с бинарной кучей, то вместо операции decreaseKey\rm decreaseKey, будем всегда просто добавлять вершину с новым ключом, если из кучи достали вершину с ключом, значение которого больше чем у нее уже стоит, просто игнорировать. Вершин в куче будет не больше 2n2 \cdot n, следовательно, операция extractMin\rm extractMin будет выполняться за O(log(n))O(log(\cdot n)), что равно O(logn)O(logn). Максимальное количество вершин, которое мы сможем достать, равняется количеству ребер, то есть m, поэтому общая асимптотика составит O(mlogn)O(mlogn), что хорошо только на разреженных графах.)

-

Фибоначчиева куча - O(VlogV+E)O(VlogV+E)

-

4.5. Алгоритм Прима. Доказательство корректности с помощью теоремы о разрезе

-

Приведем еще раз теорему о разрезе:

-
-

Для любого разбиения множества вершин V=ABV = A \cup B, существует минимальный остов, содержащий ee – минимальное по весу ребро, проходящее через разрез A,B\langle A, B \rangle

-
-

Доказательство. Возьмём минимальный остов без ee. Добавим ee, получится цикл. Два ребра этого цикла проходят через разрез A,B\langle A, B \rangle. Старое неменьше ee, удалим его. Если же в процессе построения min\rm min остова уже известно подмножество рёбер будущего min\rm min остова, эти рёбра задают компоненты связности. Мы можем сжать компоненты в вершины и для конденсации применить лемму о разрезе.

-

Следствие XX – подмножество рёбер некого минимального остова. Зафиксируем разрез V=ABV = A \cup B такой, что все рёбра XX не пересекают разрез. Тогда \exist минимальный остов, содержащий X{e}X \cup \{e\}, где ee – минимальное по весу ребро, проходящее через разрез A,B\langle A, B \rangle.

-

Доказательство корректности с помощью теоремы о разрезе

-

Разрез – текущее множество AA и дополнение VAV \setminus A.

-

4.6. Алгоритм Краскала. Доказательство корректности. Оценка времени работы

-

Алгоритм Краскала

-

Будем последовательно строить подграф FF графа GG ("растущий лес"), пытаясь на каждом шаге достроить FF до некоторого MSTMST. Начнем с того, что включим в FF все вершины графа GG. Теперь будем обходить множество E(G)E(G) в порядке неубывания весов ребер. Если очередное ребро ee соединяет вершины одной компоненты связности FF, то добавление его в остов приведет к возникновению цикла в этой компоненте связности. В таком случае, очевидно, ee не может быть включено в FF. Иначе ee соединяет разные компоненты связности FF, тогда существует S,T\langle S, T \rangle разрез такой, что одна из компонент связности составляет одну его часть, а оставшаяся часть графа — вторую. Тогда ee — минимальное ребро, пересекающее этот разрез. Значит, из леммы о безопасном ребре следует, что ee является безопасным, поэтому добавим это ребро в FF. На последнем шаге ребро соединит две оставшиеся компоненты связности, полученный подграф будет минимальным остовным деревом графа GG. Для проверки возможности добавления ребра используется система непересекающихся множеств.

-
Доказательство корректности
-

Обратимся к теореме о разрезе из Пункта 4.5 -Добавляем ребро (a,b)(a, b). Разрез – любой такой, что (a,b)(a, b) через него проходит.

-
Оценка времени работы
-

O(ElogE)O(ElogE).

-

4.7. Система непересекающихся множеств. Эвристика потенциалов без доказательства. Эвристика сжатия пути без доказательства. Почти константное время работы

-

4.8. Алгоритм Борувки. Доказательство(полное). Оценка времени работы

-

Алгоритм Борувки

-

Алгоритм состоит из нескольких шагов:

-
    -
  1. Изначально каждая вершина графа GG — тривиальное дерево, а ребра не принадлежат никакому дереву.
  2. -
  3. Для каждого дерева TT найдем минимальное инцидентное ему ребро. Добавим все такие ребра.
  4. -
  5. Повторяем шаг 2 пока в графе не останется только одно дерево TT.
  6. -
-

Данный алгоритм может работать неправильно, если в графе есть ребра равные по весу. Например, полный граф из трех вершин, вес каждого ребра равен один. В TT могут быть добавлены все три ребра. Избежать эту проблему можно, например, выбирая в первом пункте среди ребер, равных по весу, ребро с наименьшим номером.

-

Доказательство:

-

Обратимся к теореме о разрезе из Пункта 4.5 -Рёбра, которые хочет добавить Борувка, разобьём на компоненты связности. Каждая компонента – дерево, рёбра компоненты будем добавлять по одному от корня к листьям. Добавляя -очередное ребро ведущее в лист vv, используем разрез {v},V{v}\langle \{v\}, V \setminus \{v\} \rangle и лемму о разрезе.

-
Оценка времени работы
-

На ii-ой итерации внешнего цикла каждая компонента состоит как минимум из двух компонент из (i1)(i−1)-й итерации. Значит, на каждой итерации число компонент уменьшается как минимум в 2 раза. Тогда внешний цикл повторяется O(logV)O(logV) раз, так как количество компонент изначально равно количеству вершин. Что же касается внутреннего цикла, то он выполняется за O(E)O(E), где EE — количество рёбер в исходном графе. Следовательно конечное время работы алгоритма O(ElogV)O(ElogV).

-

4.9. Приближение решения задачи коммивояжера с помощью минимального остовного дерева

-
-

Алгоритм Кристофидеса или алгоритм Кристофидеса-Сердюкова — это алгоритм поиска приближённых решений задачи коммивояжёра для случаев, когда расстояния образуют метрическое пространство (симметричны и удовлетворяют неравенству треугольника).

-
-

Алгоритм является аппроксимационным алгоритмом, который гарантирует, что решения находятся в пределах 3/2 от длины оптимального решения. Алгоритм назван именем Никоса Кристофидеса и Анатолия Ивановича Сердюкова, которые независимо друг от друга нашли его в 1976, и он обладает лучшим аппроксимационным коэффициентом, который был доказан для задачи коммивояжёра на метрических пространствах общего вида, хотя известны лучшие приближения для некоторых специальных случаев.

-

Пусть G=(V,w)G=(V,w) будет представителем задачи коммивояжёра. То есть GG является полным графом на множестве вершин VV, а функция ww назначает неотрицательные вещественные веса каждому ребру графа GG. -Алгоритм можно описать на псевдокоде следующим образом:

-
    -
  1. Создаём минимальное остовное дерев TT графа GG.
  2. -
  3. Пусть OO будет набором вершин с нечётными степенями в TT. Согласно лемме о рукопожатиях, OO имеет чётное число вершин.
  4. -
  5. Находим совершенное паросочетание MM минимального веса в порождённом подграфе, заданным вершинами из OO.
  6. -
  7. Комбинируем рёбра MM и TT с образованием связного мультиграфа HH, в котором каждая вершина имеет чётную степень.
  8. -
  9. Образуем эйлеров цикл в HH.
  10. -
  11. Преобразуем цикл, найденный на предыдущем шаге, в гамильтонов цикл путём пропуска повторяющихся вершин (сокращение).
  12. -
-

5. Потоки в сетях

-

5.1. Определение сети. Определение потока. Физический смысл. Аналогия с законами Кирхгофа. Определение разреза. Понятия потока через разрез

-

Определение сети

-
-

Сеть (англ. flow network) представляет собой ориентированный граф, в котором каждое ребро имеет положительную пропускную способность (англ. capacity).

-
-

Определение потока

-
-

Потоком (англ. flow) ff в GG является действительная функция f:V×VRf: V \times V \rightarrow R, удоволетворяющая условиям:

-
-
    -
  1. f(u,v)=f(v,u)f(u,v) = −f(v,u) (антисимметричность)
  2. -
  3. f(u,v)c(u,v)f(u,v) \leq c(u,v) (ограничение пропускной способности), если ребра нет, то f(u,v)=0f(u,v)=0;
  4. -
  5. Σvf(u,v)=0\Sigma v \cdot f(u,v) = 0 для всех вершин uu, кроме ss и tt (закон сохранения потока).
  6. -
-
-

Величина потока ff определяется как f=ΣvVf(s,v)|f|=\Sigma v \in V \cdot f(s,v).

-
-

Физический смысл. Аналогия с законами Кирхгофа

-

Определение разреза

-
-

(s,t)(s,t) - разрезом (англ. s-t cut) S,T\langle S,T \rangle в сети GG называется пара множеств SS, TT, удоволетворяющих условиям: sSs \in S,tTt \in T и S=VTS = V \setminus T.

-
-

Понятия потока через разрез

-
-

Поток в разрезе (англ. flow in the cut) S,T\langle S,T \rangle обозначается f(S,T)f(S,T) и вычисляется по формуле: f(S,T)=ΣuSΣvTf(u,v)f(S,T)= \Sigma u \in S \cdot \Sigma v \in T \cdot f(u,v).

-
-

5.2. Доказательство факта, что поток через любой разрез одинаковый

-

f(S,T)=f(S,V)f(S,S)=f(S,V)=f(Ss,V)+f(s,V)=f(s,V)=ff(S,T) = f(S,V) − f(S,S) = f(S,V) = f(S \setminus s,V) + f(s,V) = f(s,V) = |f|

-

1-е равенство выполняется, так как суммы не пересекаются: f(S,V)=f(S,S)+f(S,T)f(S,V)=f(S,S)+f(S,T) -2-е равенство выполняется из-за антисимметричности: f(S,S)=f(S,S)=0f(S,S)=−f(S,S)=0 -3-е равенство выполняется, как и 1-е, из-за непересекающихся сумм -4-е равенство выполняется из-за сохранения потока

-

5.3. Понятие остаточной сети. Понятие дополняющего пути. Необходимость отсутствия дополняющего пути для максимальности потока

-

Понятие остаточной сети

-

Если задана некоторая транспортная сеть GG и поток ff, то остаточная сеть GfG_f - это сеть, состоящая из ребер с пропускными способностями, указывающими, как могут меняться потоки через ребра GG.

-

Понятие дополняющего пути

-

Для заданной транспортной сети и потока дополняющим путем (англ. augmenting path) является простой путь из истока в сток в остаточной сети.

-

Необходимость отсутствия дополняющего пути для максимальности потока

-

В случае существования дополняющего пути поток можно увеличить.

-

5.4. Теорема Форда-Фалкерсона

-
-

Теорема Форда — Фалкерсо́на — теорема о максимальном потоке в графе, тесно связанная с теоремой Менгера. Звучит так: величина максимального потока в графе путей равна величине пропускной способности его минимального разреза.

-
-

5.5. Алгоритм Форда-Фалкерсона. Поиск минимального разреза. Пример целочисленной сети, в котором алгоритм работает долго

-

Алгоритм Форда-Фалкерсона

-

Идея алгоритма заключается в следующем. Изначально величине потока присваивается значение 0: f(u,v)=0f(u,v)=0 для всех u,vVu, v \in V. Затем величина потока итеративно увеличивается посредством поиска увеличивающего пути (путь от источника ss к стоку tt, вдоль которого можно послать ненулевой поток). В данной статье рассматривается алгоритм, осуществляющий этот поиск с помощью обхода в глубину (dfs). Процесс повторяется, пока можно найти увеличивающий путь.

-

Неформальное описание:

-
    -
  1. Обнуляем все потоки. Остаточная сеть изначально совпадает с исходной сетью.
  2. -
  3. В остаточной сети находим любой путь из источника в сток. Если такого пути нет, останавливаемся.
  4. -
  5. Пускаем через найденный путь (он называется увеличивающим путём или увеличивающей цепью) максимально возможный поток: -
      -
    1. На найденном пути в остаточной сети ищем ребро с минимальной пропускной способностью cminc_{min}.
    2. -
    3. Для каждого ребра на найденном пути увеличиваем поток на cminc_{min}, а в противоположном ему — уменьшаем на cminc_{min}.
    4. -
    5. Модифицируем остаточную сеть. Для всех рёбер на найденном пути, а также для противоположных (антипараллельных) им рёбер, вычисляем новую пропускную способность. Если она стала ненулевой, добавляем ребро к остаточной сети, а если обнулилась, стираем его.
    6. -
    -
  6. -
  7. Возвращаемся на шаг 2.
  8. -
-

Поиск минимального разреза

-

Алгоритм Форда-Фалкерсона использует поиск в ширину для нахождения увеличивающего пути в сети и постепенно увеличивает поток через этот путь, пока не будет достигнут максимальный поток. Затем алгоритм ищет минимальный разрез, основываясь на значении потока.

-

5.6. Алгоритм Эдмондса-Карпа. Доказательство, что кратчайшее расстояние в остаточной сети не уменьшается

-

Алгоритм Эдмондса-Карпа

-
-

Алгоритм Эдмондса-Карпа является реализацией метода Форда-Фалкерсона, в которой в качестве дополняющего пути выбирается кратчайший по рёбрам путь в остаточной сети (длины всех рёбер равны 1).

-
-

Описание:

-
    -
  1. Положим все потоки равными нулю. Остаточная сеть изначально совпадает с исходной сетью.
  2. -
  3. В остаточной сети находим кратчайший путь из источника в сток. Если такого пути нет, останавливаемся.
  4. -
  5. Пускаем через найденный путь (он называется увеличивающим путём или увеличивающей цепью) максимально возможный поток: -
      -
    1. На найденном пути в остаточной сети ищем ребро с минимальной пропускной способностью cminc_{min}.
    2. -
    3. Для каждого ребра на найденном пути увеличиваем поток на cminc_{min}, а в противоположном ему — уменьшаем на cminc_{min}.
    4. -
    5. Модифицируем остаточную сеть. Для всех рёбер на найденном пути, а также для противоположных им рёбер, вычисляем новую пропускную способность. Если она стала ненулевой, добавляем ребро к остаточной сети, а если обнулилась, стираем его.
    6. -
    -
  6. -
  7. Возвращаемся на шаг 2.
  8. -
-

Доказательство, что кратчайшее расстояние в остаточной сети не уменьшаетcя:

-

Лемма:

-
-

Кратчайшее расстояние от истока до каждой вершины не уменьшается с выполнением каждой итерации, т.е. leveli+1[v]>=leveli[v]level_{i+1}[v] >= level_i[v], где нижний индекс обозначает номер фазы, перед которой взяты значения этих переменных.

-
-

Доказательство:

-

Зафиксируем произвольную фазу ii и произвольную вершину vv и рассмотрим любой кратчайший svs - v путь PP в сети Gi+1RG^R_{i+1} (напомним, так мы обозначаем остаточную сеть, взятую перед выполнением i+1i+1-ой фазы). Очевидно, длина пути PP равна leveli+1[v]level_{i+1}[v].

-

Заметим, что в остаточную сеть Gi+1RG^R_{i+1} могут входить только рёбра из GRG^R, а также рёбра, обратные рёбрам из GRG^R (это следует из определения остаточной сети). Рассмотрим два случая:

-

Путь PP содержит только рёбра из GRG^R. Тогда, понятно, длина пути PP больше либо равна leveli[v]level_i[v] (потому что leveli[v]level_i[v] по определению — длина кратчайшего пути), что и означает выполнение неравенства. Путь PP содержит как минимум одно ребро, не содержащееся в GRG^R (но обратное какому-то ребру из GRG^R). Рассмотрим первое такое ребро; пусть это будет ребро (u,w)(u,w). suws \rightarrow u \rightarrow w

-

Мы можем применить нашу лемму к вершине uu, потому что она подпадает под первый случай; итак, мы получаем неравенство leveli+1[u]>=leveli[u]level_{i+1}[u] >= level_i[u].

-

Теперь заметим, что поскольку ребро (u,w)(u, w) появилось в остаточной сети только после выполнения ii-ой фазы, то отсюда следует, что вдоль ребра (w,u)(w, u) был дополнительно пропущен какой-то поток; следовательно, ребро (w,u)(w, u) принадлежало слоистой сети перед ii-ой фазой, а потому leveli[u]=leveli[w]+1level_i[u] = level_i[w] + 1. Учтём, что по свойству кратчайших путей leveli+1[w]=leveli+1[u]+1level_{i+1}[w] = level_{i+1}[u] + 1, и объединяя это равенство с двумя предыдущими неравенствами, получаем: -leveli+1[w]>=leveli[w]+2level_{i+1}[w] >= level_i[w] + 2.

-

Теперь мы можем применять те же самые рассуждения ко всему оставшемуся пути до vv (т.е. что каждое инвертированное ребро добавляет к level\rm level как минимум два), и в итоге получим требуемое неравенство.

-

5.7. Общая оценка времени работы алгоритма Эдмондса-Карпа

-

Сложность алгоритма Эдмондса-Карпа равна O(VE2)O(VE^2).

-

5.8. Слоистая сеть. Алгоритм Диница. Оценка времени работы без доказательства

-

Слоистая сеть

-
-

Слоистая сеть — это сеть состоящая только из таких рёбер (v,u)(v, u) сети GG, что d(v)+1=d(u)d(v) + 1 = d(u), где dd - расстояние от истока.

-
-

Алгоритм Диница

-

Схема алгоритма:

-
    -
  1. Построить слоистую сеть по остаточной, если сток не достижим то завершить алгоритм
  2. -
  3. Пустить блокирующий поток в слоистой сети
  4. -
  5. Если алгоритм завершился, то в остаточной сети сток не достижим из истока, а значит не существует увеличивающего пути, следовательно найденный поток максимален.
  6. -
-

Оценка времени работы без доказательства

-

Так как расстояние строго увеличивается количество итераций алгоритма = O(V)O(V). В зависимости от выбора алгоритма поиска блокирующего потока время данного алгоритма может составить O(VE2),O(V2E)O(VE^2), O(V^2E) или O(V3)O(V^3).

-

6. RMQ. Sparse-table, дерево отрезков. LCA. Декартово дерево по неявному ключу

-

6.1. RSQ и RMQ. Sparse-table

-

RSQ и RMQ.

-

Дан числовой массив: A[0]A[0], A[1]A[1], ..., A[N1A[N−1.$

-

Нужно отвечать на запросы:

-
    -
  • для данных LL и RR вычислить сумму A[L]+...+A[R1]A[L] + ... + A[R−1];
  • -
  • (опционально) обновить элемент: A[i]:=xA[i] := x.
  • -
-

Операция — не обязательно сумма, а элементы — не обязательно числа.

-
    -
  • Сумма \rightarrow RSQ (range sum query)
  • -
  • Минимум \rightarrow RMQ (range minimum query)
  • -
-

6.2. Дерево отрезков. Обработка запросов от листьев. Обработка запросов от корня

-

Дерево отрезков

-

Дерево отрезков — это структура данных, которая позволяет эффективно (т.е. за асимптотику O(logn)O(log n)) реализовать операции следующего вида: нахождение суммы/минимума элементов массива в заданном отрезке (a[l...r]a[l ... r], где ll и rr поступают на вход алгоритма), при этом дополнительно возможно изменение элементов массива: как изменение значения одного элемента, так и изменение элементов на целом подотрезке массива (т.е. разрешается присвоить всем элементам a[l...r]a[l ... r] какое-либо значение, либо прибавить ко всем элементам массива какое-либо число).

-

Вообще, дерево отрезков — очень гибкая структура, и число задач, решаемых ей, теоретически неограниченно. Помимо приведённых выше видов операций с деревьями отрезков, также возможны и гораздо более сложные операции (см. раздел "Усложнённые версии дерева отрезков"). В частности, дерево отрезков легко обобщается на большие размерности: например, для решения задачи о поиске суммы/минимума в некотором подпрямоугольнике данной матрицы (правда, уже только за время O(log2n)O(log^2 n)).

-

Важной особенностью деревьев отрезков является то, что они потребляют линейный объём памяти: стандартному дереву отрезков требуется порядка 4n4n элементов памяти для работы над массивом размера nn.

-

Обработка запросов от листьев. Обработка запросов от корня

-

В дереве отрезков обработка запросов может осуществляться как от листьев (bottom-up), так и от корня (top-down). Это связано с тем, как информация распространяется вдоль дерева при выполнении запросов.

-
    -
  1. -

    Обработка запросов от листьев (Bottom-Up):

    -
      -
    • Идея заключается в том, что сначала вычисляются значения для листьев (первоначальные элементы массива), а затем эта информация постепенно передается вверх по дереву для расчета значений на более высоких уровнях.
    • -
    • Когда поступает запрос на определенный отрезок, начинаются вычисления от соответствующих листьев. Затем результаты объединяются на более высоких уровнях дерева, пока не достигнется корень.
    • -
    • Этот подход обеспечивает эффективность расчетов для запросов на отрезках.
    • -
    -
  2. -
  3. -

    Обработка запросов от корня (Top-Down):

    -
      -
    • Здесь запрос начинается с корня дерева, и информация передается вниз по дереву до тех пор, пока не достигнут нужные листья (или пока не выполнены условия запроса).
    • -
    • Этот метод может быть полезен в случаях, когда запросы связаны с каким-то условием, которое позволяет пропускать некоторые поддеревья, не вычисляя их полностью.
    • -
    • Позволяет оптимизировать запросы, если известно, что результаты на некоторых уровнях дерева не повлияют на результат запроса.
    • -
    -
  4. -
-

Реализация обработки запросов от листьев или от корня зависит от конкретных требований задачи и структуры дерева отрезков. В некоторых случаях может быть полезно использовать комбинацию обоих методов для оптимизации производительности и удовлетворения специфических требований запросов.

-

6.3. Изменение значения в массиве, обновление дерева отрезков. Множественные операции

-

Изменение значения в массиве, обновление дерева отрезков.

-

Напомним, что запрос обновления получает на вход индекс ii и значение xx, и перестраивает дерево отрезков таким образом, чтобы оно соответствовало новому значению A[i]:=xA[i] := x. Этот запрос должен также выполняться за время O(logn)O(log n).

-

Это более простой запрос по сравнению с запросом подсчёта суммы. Дело в том, что элемент A[i]A[i] участвует только в относительно небольшом числе вершин дерева отрезков: а именно, в O(logn)O(log n) вершинах — по одной с каждого уровня.

-

Тогда понятно, что запрос обновления можно реализовать как рекурсивную функцию: ей передаётся текущая вершина дерева отрезков, и эта функция выполняет рекурсивный вызов от одного из двух своих сыновей (от того, который содержит позицию i в своём отрезке), а после этого — пересчитывает значение суммы в текущей вершине точно таким же образом, как мы это делали при построении дерева отрезков (т.е. как сумма значений по обоим сыновьям текущей вершины).

-
void update (int v, int tl, int tr, int pos, int new_val) {
-  if (tl == tr)
-    t[v] = new_val;
-  else {
-    int tm = (tl + tr) / 2;
-    if (pos <= tm)
-      update (v*2, tl, tm, pos, new_val);
-    else
-      update (v*2+1, tm+1, tr, pos, new_val);
-    t[v] = t[v*2] + t[v*2+1];
-  }
-}
-
-

Множественные операции

-

Множественные операции в дереве отрезков относятся к расширенной концепции структуры данных "дерево отрезков", которая позволяет эффективно выполнять несколько операций на отрезках массива или последовательности данных. Обычно дерево отрезков предназначено для выполнения операций типа запроса на сумму, минимум, максимум на определенном подотрезке массива. Однако, при необходимости, можно расширить возможности дерева отрезков для поддержки нескольких операций.

-

Примеры множественных операций в дереве отрезков могут включать в себя:

-
    -
  1. -

    Сумма и обновление (Range Sum Query and Range Update): Помимо запросов на сумму элементов на определенном отрезке, можно добавить операции обновления, изменяющие значения элементов на определенном отрезке.

    -
  2. -
  3. -

    Минимум/максимум и обновление (Range Minimum/Maximum Query and Range Update): Поддержка запросов на поиск минимума или максимума на определенном отрезке, а также операций обновления значений элементов.

    -
  4. -
  5. -

    Подсчет количества элементов в диапазоне с заданным условием (Count Elements in Range with a Given Condition): Дерево отрезков может быть настроено для подсчета количества элементов, удовлетворяющих определенному условию, на определенном отрезке.

    -
  6. -
  7. -

    Операции нахождения k-го порядкового статистического элемента (K-th Order Statistics Operations): Поиск kk-го минимального/максимального элемента на определенном отрезке.

    -
  8. -
  9. -

    Сложные агрегатные функции: Различные агрегатные функции, такие как нахождение среднего значения, медианы и др., могут быть реализованы с использованием множественных операций.

    -
  10. -
-

Реализация множественных операций требует изменений в структуре дерева отрезков и логике обработки запросов и обновлений. Это может потребовать более сложного кода, но в то же время предоставляет более широкий спектр возможностей для решения различных задач.

-

6.4. LCA. Метод двоичного подъёма

-

LCA

-

Наименьший общий предок (нижайший общий предок) вершин uu и vv в корневом дереве TT — наиболее удалённая от корня дерева вершина, лежащая на обоих путях от uu и vv до корня, т. е. являющаяся предком обеих вершин. Общепринятое сокращение — LCALCA от англ. lowest (least) common ancestor.

-

Метод двоичного подъёма

-
-

Метод двоичного подъёма — один из самых простых методов для решения задачи LCA в online. Он не использует метод решения задачи RMQ и основан на методе динамического программирования.

-
-
function preprocess():
-     int[] p = dfs(0)
-     for i = 1 to n
-        dp[i][0] = p[i]
-     for j = 1 to log(n)
-        for i = 1 to n
-           dp[i][j] = dp[dp[i][j - 1]][j - 1]
-  
-  int lca(int v, int u):
-     if d[v] > d[u]
-        swap(v, u)
-     for i = log(n) downto 0
-        if d[dp[u][i]] - d[v] >= 0
-           u = dp[u][i]
-     if v == u
-        return v
-     for i = log(n) downto 0
-        if dp[v][i] != dp[u][i]
-           v = dp[v][i]
-           u = dp[u][i]
-     return p[v]
-
-

6.5. Декартово дерево по неявному ключу. Интерфейс быстрого массива: Доступ к элементу в позиции i, Вставка элемента в позицию i, Удаление элемента из позиции i, Конкатенация двух массивов, разделение массива на два

-

Декартово дерево по неявному ключу

-

Декартово дерево по неявному ключу - это структура данных, объединяющая свойства декартова дерева и быстрого массива (cartesian tree on implicit key). Декартово дерево основано на двух параметрах: приоритете (обычно случайном) и ключе элемента. В случае декартова дерева по неявному ключу, элементы упорядочены по порядку, заданному индексами массива.

-

Интерфейс быстрого массива

-
    -
  1. -

    Доступ к элементу в позиции i:

    -
      -
    • Так как элементы упорядочены по неявному ключу (порядковому номеру), доступ к элементу в позиции i выполняется, обращаясь к элементу с ключом i.
    • -
    -
  2. -
  3. -

    Вставка элемента в позицию i:

    -
      -
    • Вставка элемента в позицию i может быть выполнена путем создания нового узла с соответствующим приоритетом и неявным ключом i, затем соединения этого нового узла с существующими поддеревьями вокруг позиции i.
    • -
    -
  4. -
  5. -

    Удаление элемента из позиции i:

    -
      -
    • Удаление элемента из позиции i подразумевает разделение декартова дерева на два поддерева: одно до позиции i и второе после позиции i. Затем оба поддерева могут быть объединены, игнорируя узел с ключом i.
    • -
    -
  6. -
-

Декартово дерево по неявному ключу обеспечивает эффективный доступ к элементам по индексу, а также эффективную вставку и удаление элементов, поддерживая при этом свойства декартова дерева.

-

Конкатенация двух массивов

-

Декартово дерево можно использовать для эффективной конкатенации двух массивов и разделения массива на две части, представляя массив в виде декартова дерева по неявному ключу.

-

Для конкатенации двух массивов, представленных в виде декартова дерева, можно выполнить следующие шаги:

-
    -
  1. Создать новый узел, который станет корнем нового дерева.
  2. -
  3. Как корень нового дерева выбрать узел с максимальным приоритетом из обоих деревьев.
  4. -
  5. Сделать корень дерева с максимальным приоритетом корнем нового дерева.
  6. -
  7. Поддерево с меньшим приоритетом становится левым (или правым) поддеревом корня нового дерева.
  8. -
  9. Рекурсивно повторить процесс для поддеревьев, соответствующих двум исходным деревьям.
  10. -
-

Разделение массива на два

-

Для разделения массива на две части в декартовом дереве, можно воспользоваться тем фактом, что приоритеты узлов определяют порядок элементов. Мы ищем узел, чей неявный ключ соответствует индексу разделения.

-
    -
  1. Выполняем поиск в декартовом дереве по индексу разделения.
  2. -
  3. Получаем поддерево с корнем, у которого неявный ключ равен индексу разделения.
  4. -
  5. Разделяем это поддерево на два: левое поддерево становится левой частью, а правое поддерево становится правой частью.
  6. -
- - - - - \ No newline at end of file diff --git a/doc/Theory.md b/doc/Theory.md deleted file mode 100644 index 657d1c4..0000000 --- a/doc/Theory.md +++ /dev/null @@ -1,1352 +0,0 @@ -# Теория по алгоритмам - -## Содержание - -- [Содержание](#содержание) -- [1. Обходы графа](#1-обходы-графа) - - [1.1. Ориентированный граф, псевдограф. Неориентированный граф, псевдограф. Связность в неориентированном графе, компоненты связности. Слабая и сильная связность в ориентированном графе. Компоненты слабой, сильной связности](#11-ориентированный-граф-псевдограф-неориентированный-граф-псевдограф-связность-в-неориентированном-графе-компоненты-связности-слабая-и-сильная-связность-в-ориентированном-графе-компоненты-слабой-сильной-связности) - - [Ориентированный граф](#ориентированный-граф) - - [Неориентированный граф](#неориентированный-граф) - - [1.2. Обход в глубину. Цвета вершин. Времена входа и выхода. Лемма о белых путях (с доказательством)](#12-обход-в-глубину-цвета-вершин-времена-входа-и-выхода-лемма-о-белых-путях-с-доказательством) - - [Обход в глубину](#обход-в-глубину) - - [Лемма о белых путях](#лемма-о-белых-путях) - - [1.3. Проверка связности неориентированного графа. Поиск цикла в неориентированном и ориентированном графе. Топологическая сортировка](#13-проверка-связности-неориентированного-графа-поиск-цикла-в-неориентированном-и-ориентированном-графе-топологическая-сортировка) - - [Проверка связности неориентированного графа](#проверка-связности-неориентированного-графа) - - [Поиск цикла в неориентированном и ориентированном графе](#поиск-цикла-в-неориентированном-и-ориентированном-графе) - - [Топологическая сортировка](#топологическая-сортировка) - - [1.4. Нахождение компонент сильной связности. Алгоритм Косарайю с доказательством корректности(концепция). Алгоритм Тарьяна без доказательства корректности](#14-нахождение-компонент-сильной-связности-алгоритм-косарайю-с-доказательством-корректностиконцепция-алгоритм-тарьяна-без-доказательства-корректности) - - [Нахождение компонент сильной связности](#нахождение-компонент-сильной-связности) - - [Алгоритм Косарайю с доказательством корректности(концепция)](#алгоритм-косарайю-с-доказательством-корректностиконцепция) - - [Алгоритм Тарьяна без доказательства корректности](#алгоритм-тарьяна-без-доказательства-корректности) - - [1.5. Компоненты реберной двусвязности. Мосты. Поиск мостов](#15-компоненты-реберной-двусвязности-мосты-поиск-мостов) - - [Компоненты реберной двусвязности](#компоненты-реберной-двусвязности) - - [Мосты](#мосты) - - [Поиск мостов](#поиск-мостов) - - [1.6. Компоненты вершинной двусвязности. Точки сочленения. Поиск точек сочленения](#16-компоненты-вершинной-двусвязности-точки-сочленения-поиск-точек-сочленения) - - [Компоненты вершинной двусвязности](#компоненты-вершинной-двусвязности) - - [Точки сочленения](#точки-сочленения) - - [Поиск точек сочленения](#поиск-точек-сочленения) - - [1.7. Волновой алгоритм. Обход в ширину (применение очереди в волновом алгоритме)](#17-волновой-алгоритм-обход-в-ширину-применение-очереди-в-волновом-алгоритме) - - [Волновой алгоритм](#волновой-алгоритм) - - [Обход в ширину (применение очереди в волновом алгоритме)](#обход-в-ширину-применение-очереди-в-волновом-алгоритме) - - [1.8. Критерий существования Эйлерова пути и цикла в ориентированном и неориентированном графе. Поиск эйлерова пути и цикла](#18-критерий-существования-эйлерова-пути-и-цикла-в-ориентированном-и-неориентированном-графе-поиск-эйлерова-пути-и-цикла) - - [Критерий существования Эйлерова пути и цикла в неориентированном графе](#критерий-существования-эйлерова-пути-и-цикла-в-неориентированном-графе) - - [Критерий существования Эйлерова пути и цикла в ориентированном графе](#критерий-существования-эйлерова-пути-и-цикла-в-ориентированном-графе) - - [Поиск эйлерова пути и цикла](#поиск-эйлерова-пути-и-цикла) -- [2. Планарность графа](#2-планарность-графа) - - [2.1. Формула Эйлера. Теорема Портнягина-Куратовского. Теорема Вагнера](#21-формула-эйлера-теорема-портнягина-куратовского-теорема-вагнера) - - [Формула Эйлера](#формула-эйлера) - - [Теорема Портнягина-Куратовского](#теорема-портнягина-куратовского) - - [Теорема Вагнера](#теорема-вагнера) - - [2.2. Гамма алгоритм. Контактная вершина](#22-гамма-алгоритм-контактная-вершина) - - [Гамма алгоритм](#гамма-алгоритм) - - [2.3. Теорема о корректности Гамма алгоритма. Асимптотика алгоритма](#23-теорема-о-корректности-гамма-алгоритма-асимптотика-алгоритма) - - [Теорема о корректности Гамма алгоритма](#теорема-о-корректности-гамма-алгоритма) -- [3. Кратчайшие пути во взвешенном графе](#3-кратчайшие-пути-во-взвешенном-графе) - - [3.1. Алгоритм Дейкстры. Доказательство корректности(с доказательством). Оценка времени работы. Дерево кратчайших путей](#31-алгоритм-дейкстры-доказательство-корректностис-доказательством-оценка-времени-работы-дерево-кратчайших-путей) - - [Алгоритм Дейкстры](#алгоритм-дейкстры) - - [Доказательство корректности](#доказательство-корректности) - - [Оценка времени работы](#оценка-времени-работы) - - [Дерево кратчайших путей](#дерево-кратчайших-путей) - - [3.2. Потенциалы. Условие применимости алгоритма Дейкстры для измененных длин ребер. Потенциал $\\pi(v) = \\rho(v, t)$](#32-потенциалы-условие-применимости-алгоритма-дейкстры-для-измененных-длин-ребер-потенциал-piv--rhov-t) - - [Потенциалы](#потенциалы) - - [Условие применимости алгоритма Дейкстры для измененных длин ребер](#условие-применимости-алгоритма-дейкстры-для-измененных-длин-ребер) - - [Потенциал $\\pi(v) = \\rho(v, t)$](#потенциал-piv--rhov-t) - - [3.3. Алгоритм A\*. Условие монотонности на эвристику. Примеры эвристик](#33-алгоритм-a-условие-монотонности-на-эвристику-примеры-эвристик) - - [Алгоритм A\*](#алгоритм-a) - - [Примеры эвристик](#примеры-эвристик) - - [3.4. Алгоритм Форда-Беллмана. Хранение в матрице: $D\_{vk}$ равно длине кратчайшего пути до вершины $v$ за ровно $k$ ребер (не более $k$ ребер). Доказательство корректности (полное). Оценка времени работы](#34-алгоритм-форда-беллмана-хранение-в-матрице-d_vk-равно-длине-кратчайшего-пути-до-вершины-v-за-ровно-k-ребер-не-более-k-ребер-доказательство-корректности-полное-оценка-времени-работы) - - [Доказательство корректности(полное)](#доказательство-корректностиполное) - - [Оценка времени работы](#оценка-времени-работы-1) - - [3.5. Восстановление пути. Детектирование цикла отрицательного веса. Поиск самого цикла](#35-восстановление-пути-детектирование-цикла-отрицательного-веса-поиск-самого-цикла) - - [Восстановление пути](#восстановление-пути) - - [Детектирование цикла отрицательного веса](#детектирование-цикла-отрицательного-веса) - - [Поиск самого цикла](#поиск-самого-цикла) - - [3.6. Нахождение кратчайших путей с учетом циклов отрицательного веса](#36-нахождение-кратчайших-путей-с-учетом-циклов-отрицательного-веса) - - [3.7. Алгоритм Флойда. Доказательство (концепция). Восстановление пути](#37-алгоритм-флойда-доказательство-концепция-восстановление-пути) - - [Алгоритм Флойда](#алгоритм-флойда) - - [Доказательство (концепция)](#доказательство-концепция) - - [Восстановление пути](#восстановление-пути-1) - - [3.8. Нахождение цикла отрицательного веса](#38-нахождение-цикла-отрицательного-веса) - - [3.9. Алгоритм Джонсона. Добавление фиктивного корня и фиктивных ребер для запуска алгоритма Форда-Беллмана](#39-алгоритм-джонсона-добавление-фиктивного-корня-и-фиктивных-ребер-для-запуска-алгоритма-форда-беллмана) - - [Алгоритм Джонсона](#алгоритм-джонсона) - - [Добавление фиктивного корня и фиктивных ребер для запуска алгоритма Форда-Беллмана](#добавление-фиктивного-корня-и-фиктивных-ребер-для-запуска-алгоритма-форда-беллмана) -- [4. Остовные деревья](#4-остовные-деревья) - - [4.1. Остовное дерево. Построение с помощью обхода в глубину и в ширину](#41-остовное-дерево-построение-с-помощью-обхода-в-глубину-и-в-ширину) - - [Остовное дерево](#остовное-дерево) - - [Построение с помощью обхода в глубину и в ширину](#построение-с-помощью-обхода-в-глубину-и-в-ширину) - - [4.2. Определение минимального остовного дерева](#42-определение-минимального-остовного-дерева) - - [4.3. Теорема о разрезе. Доказательство](#43-теорема-о-разрезе-доказательство) - - [Теорема о разрезе](#теорема-о-разрезе) - - [4.4. Алгоритм Прима. Аналогия с алгоритмом Дейкстры. Оценка времени работы для различных реализаций очереди с приоритетом: бинарная куча, Фибоначчиева куча (последнее без доказательства)](#44-алгоритм-прима-аналогия-с-алгоритмом-дейкстры-оценка-времени-работы-для-различных-реализаций-очереди-с-приоритетом-бинарная-куча-фибоначчиева-куча-последнее-без-доказательства) - - [Алгоритм Прима](#алгоритм-прима) - - [Аналогия с алгоритмом Дейкстры](#аналогия-с-алгоритмом-дейкстры) - - [Оценка времени работы для различных реализаций очереди с приоритетом: бинарная куча, Фибоначчиева куча](#оценка-времени-работы-для-различных-реализаций-очереди-с-приоритетом-бинарная-куча-фибоначчиева-куча) - - [4.5. Алгоритм Прима. Доказательство корректности с помощью теоремы о разрезе](#45-алгоритм-прима-доказательство-корректности-с-помощью-теоремы-о-разрезе) - - [Доказательство корректности с помощью теоремы о разрезе](#доказательство-корректности-с-помощью-теоремы-о-разрезе) - - [4.6. Алгоритм Краскала. Доказательство корректности. Оценка времени работы](#46-алгоритм-краскала-доказательство-корректности-оценка-времени-работы) - - [Алгоритм Краскала](#алгоритм-краскала) - - [Доказательство корректности](#доказательство-корректности-1) - - [Оценка времени работы](#оценка-времени-работы-2) - - [4.7. Система непересекающихся множеств. Эвристика потенциалов без доказательства. Эвристика сжатия пути без доказательства. Почти константное время работы](#47-система-непересекающихся-множеств-эвристика-потенциалов-без-доказательства-эвристика-сжатия-пути-без-доказательства-почти-константное-время-работы) - - [4.8. Алгоритм Борувки. Доказательство(полное). Оценка времени работы](#48-алгоритм-борувки-доказательствополное-оценка-времени-работы) - - [Алгоритм Борувки](#алгоритм-борувки) - - [Оценка времени работы](#оценка-времени-работы-3) - - [4.9. Приближение решения задачи коммивояжера с помощью минимального остовного дерева](#49-приближение-решения-задачи-коммивояжера-с-помощью-минимального-остовного-дерева) -- [5. Потоки в сетях](#5-потоки-в-сетях) - - [5.1. Определение сети. Определение потока. Физический смысл. Аналогия с законами Кирхгофа. Определение разреза. Понятия потока через разрез](#51-определение-сети-определение-потока-физический-смысл-аналогия-с-законами-кирхгофа-определение-разреза-понятия-потока-через-разрез) - - [Определение сети](#определение-сети) - - [Определение потока](#определение-потока) - - [Физический смысл. Аналогия с законами Кирхгофа](#физический-смысл-аналогия-с-законами-кирхгофа) - - [Определение разреза](#определение-разреза) - - [Понятия потока через разрез](#понятия-потока-через-разрез) - - [5.2. Доказательство факта, что поток через любой разрез одинаковый](#52-доказательство-факта-что-поток-через-любой-разрез-одинаковый) - - [5.3. Понятие остаточной сети. Понятие дополняющего пути. Необходимость отсутствия дополняющего пути для максимальности потока](#53-понятие-остаточной-сети-понятие-дополняющего-пути-необходимость-отсутствия-дополняющего-пути-для-максимальности-потока) - - [Понятие остаточной сети](#понятие-остаточной-сети) - - [Понятие дополняющего пути](#понятие-дополняющего-пути) - - [Необходимость отсутствия дополняющего пути для максимальности потока](#необходимость-отсутствия-дополняющего-пути-для-максимальности-потока) - - [5.4. Теорема Форда-Фалкерсона](#54-теорема-форда-фалкерсона) - - [5.5. Алгоритм Форда-Фалкерсона. Поиск минимального разреза. Пример целочисленной сети, в котором алгоритм работает долго](#55-алгоритм-форда-фалкерсона-поиск-минимального-разреза-пример-целочисленной-сети-в-котором-алгоритм-работает-долго) - - [Алгоритм Форда-Фалкерсона](#алгоритм-форда-фалкерсона) - - [Поиск минимального разреза](#поиск-минимального-разреза) - - [5.6. Алгоритм Эдмондса-Карпа. Доказательство, что кратчайшее расстояние в остаточной сети не уменьшается](#56-алгоритм-эдмондса-карпа-доказательство-что-кратчайшее-расстояние-в-остаточной-сети-не-уменьшается) - - [Алгоритм Эдмондса-Карпа](#алгоритм-эдмондса-карпа) - - [5.7. Общая оценка времени работы алгоритма Эдмондса-Карпа](#57-общая-оценка-времени-работы-алгоритма-эдмондса-карпа) - - [5.8. Слоистая сеть. Алгоритм Диница. Оценка времени работы без доказательства](#58-слоистая-сеть-алгоритм-диница-оценка-времени-работы-без-доказательства) - - [Слоистая сеть](#слоистая-сеть) - - [Алгоритм Диница](#алгоритм-диница) - - [Оценка времени работы без доказательства](#оценка-времени-работы-без-доказательства) -- [6. RMQ. Sparse-table, дерево отрезков. LCA. Декартово дерево по неявному ключу](#6-rmq-sparse-table-дерево-отрезков-lca-декартово-дерево-по-неявному-ключу) - - [6.1. RSQ и RMQ. Sparse-table](#61-rsq-и-rmq-sparse-table) - - [RSQ и RMQ.](#rsq-и-rmq) - - [6.2. Дерево отрезков. Обработка запросов от листьев. Обработка запросов от корня](#62-дерево-отрезков-обработка-запросов-от-листьев-обработка-запросов-от-корня) - - [Дерево отрезков](#дерево-отрезков) - - [Обработка запросов от листьев. Обработка запросов от корня](#обработка-запросов-от-листьев-обработка-запросов-от-корня) - - [6.3. Изменение значения в массиве, обновление дерева отрезков. Множественные операции](#63-изменение-значения-в-массиве-обновление-дерева-отрезков-множественные-операции) - - [Изменение значения в массиве, обновление дерева отрезков.](#изменение-значения-в-массиве-обновление-дерева-отрезков) - - [Множественные операции](#множественные-операции) - - [6.4. LCA. Метод двоичного подъёма](#64-lca-метод-двоичного-подъёма) - - [LCA](#lca) - - [Метод двоичного подъёма](#метод-двоичного-подъёма) - - [6.5. Декартово дерево по неявному ключу. Интерфейс быстрого массива: Доступ к элементу в позиции i, Вставка элемента в позицию i, Удаление элемента из позиции i, Конкатенация двух массивов, разделение массива на два](#65-декартово-дерево-по-неявному-ключу-интерфейс-быстрого-массива-доступ-к-элементу-в-позиции-i-вставка-элемента-в-позицию-i-удаление-элемента-из-позиции-i-конкатенация-двух-массивов-разделение-массива-на-два) - - [Декартово дерево по неявному ключу](#декартово-дерево-по-неявному-ключу) - - [Интерфейс быстрого массива](#интерфейс-быстрого-массива) - - [Конкатенация двух массивов](#конкатенация-двух-массивов) - - [Разделение массива на два](#разделение-массива-на-два) - -
- -## 1. Обходы графа - -### 1.1. Ориентированный граф, псевдограф. Неориентированный граф, псевдограф. Связность в неориентированном графе, компоненты связности. Слабая и сильная связность в ориентированном графе. Компоненты слабой, сильной связности - -#### Ориентированный граф - -> **Ориентированным графом** (англ. *directed graph*) $G$ называется пара $G = (V, E)$, где $V$ — множество вершин (англ. vertices), а $E \subset V \times V$ — множество рёбер. - -> **Псевдограф(*мультиорграф*)** — это ориентированный граф, в котором разрешены кратные дуги, то есть дуги, имеющие те же начальные и конечные вершины. - -**Псевдографом** $G$ называется упорядоченная пара $G := (V, A)$, в которой $V$ — множество вершин, $A$ — мультимножество упорядоченных пар вершин. Элементы этого множества называются дугами. - -> Ориентированный граф называется **слабо связным**, если соответствующий неориентированный граф является связным; - -> Ориентированный граф называется **сильно связным**, если всякая вершина $v$ достижима из любой другой вершины $v$; - -> **Компонентой сильной связности** ориентированного графа называется максимальный по включению сильно связный подграф. Другими словами, это подграф, любые две вершины которого принадлежат какому-либо циклу, и содержащий все такие циклы для своих вершин. - -> **Компонентой слабой связности** ориентированного графа называют его максимальный слабо связный подграф. - -#### Неориентированный граф - -> **Неориентированным графом** (англ. *undirected graph*) $G$ называется пара $G = (V, E)$, где $V$ — множество вершин, а $E \subset {{v,u} : v,u \in V}$ — множество рёбер. - -Формально, псевдографом (*мультифграфом*) $G$ называется упорядоченная пара $G := (V, E)$, в которой $V$ — множество вершин, $E$ — мультимножество неупорядоченных пар вершин. Элементы этого множества называются рёбрами. - -> Неориентированный граф называется *связным*, если все его вершины достижимы из некоторой вершины (эквивалентно, из любой его вершины). - -> **Компонентой связности** неориентированного графа называется максимальный по включению связный подграф - -### 1.2. Обход в глубину. Цвета вершин. Времена входа и выхода. Лемма о белых путях (с доказательством) - -#### Обход в глубину - -> **Обход в глубину** (поиск в глубину, англ. *Depth-First Search*, *DFS*) — один из основных методов обхода графа, часто используемый для проверки связности, поиска цикла и компонент сильной связности и для топологической сортировки. - -```c++ -#include - -std::vector> graph; // граф -int vertex_count; // число вершин - -std::vector color; // цвет вершины (0, 1, или 2) - -std::vector time_in, time_out; // "времена" захода и выхода из вершины -int dfs_timer = 0; // "таймер" для определения времён - -void dfs (int vertex) { - time_in[vertex] = dfs_timer++; - color[vertex] = 1; - for (int u : graph[vertex]) - if (color[u] == 0) - dfs(u); - color[v] = 2; - time_out[v] = dfs_timer++; -} -``` - -#### Лемма о белых путях - -> Лемма: Не существует такого момента выполнения поиска в глубину, в который бы существовало ребро из черной вершины в белую. - -**Доказательство**: Пусть в процессе выполнения процедуры DFS нашлось ребро из черной вершины $v$ в белую вершину u. Рассмотрим момент времени, когда мы запустили DFS(v). В этот момент вершина $v$ была перекрашена из белого в серый, а вершина u была белая. Далее в ходе выполнения алгоритма будет запущен DFS(u), поскольку обход в глубину обязан посетить все белые вершины, в которые есть ребро из v. По алгоритму вершина $v$ будет покрашена в черный цвет тогда, когда завершится обход всех вершин, достижимых из нее по одному ребру, кроме тех, что были рассмотрены раньше нее. Таким образом, вершина $v$ может стать черной только тогда, когда DFS выйдет из вершины u, и она будет покрашена в черный цвет. Получаем противоречие. - -### 1.3. Проверка связности неориентированного графа. Поиск цикла в неориентированном и ориентированном графе. Топологическая сортировка - -#### Проверка связности неориентированного графа - -Небольшая модификация алгоритма обхода в глубину, в которой будем возвращать количество посещенных вершин. Запустим такой **DFS** от некоторой вершины графа $G$, если его результат равен $|V|$, то мы побывали во всех вершинах графа, а следовательно он связен, иначе какие-то вершины остались непосещенными. Работает алгоритм за $O(|V|+|E|)$ - -```c++ -std::vector> graph; // граф - -// visited — массив цветов вершин -int dfs(int vertex, std::vector visited): - int visitedVertices = 1 - visited[vertex] = true // помечаем вершину как пройденную - for (auto u : graph[vertex]) // проходим по смежным с u вершинам - // проверяем, не находились ли мы ранее в выбранной вершине - if (!visited[v]) - visitedVertices += dfs(v, visited) - return visitedVertices -``` - -#### Поиск цикла в неориентированном и ориентированном графе - -*Условие:* - -Пусть дан ориентированный или неориентированный граф без петель и кратных рёбер. Требуется проверить, является ли он ациклическим, а если не является, то найти любой цикл. - -*Решение:* - -Произведём серию поисков в глубину в графе. Т.е. из каждой вершины, в которую мы ещё ни разу не приходили, запустим поиск в глубину, который при входе в вершину будет красить её в серый цвет, а при выходе - в чёрный. И если поиск в глубину пытается пойти в серую вершину, то это означает, что мы нашли цикл (если граф неориентированный, то случаи, когда поиск в глубину из какой-то вершины пытается пойти в предка, не считаются). - -```c++ -#include -#include - -int n; -std::vector> graph; -std::vector color; -std::vector parent; -int cycle_start, cycle_end; - -bool dfs(int vertex) { - color[vertex] = 1; - for (size_t i = 0; i < graph[vertex].size(); ++i) { - int $to$ = graph[vertex][i]; - if (color[to] == 0) { - parent[to] = vertex; - if (dfs(to)) return true; - } - else if (color[to] == 1) { - cycle_end = vertex; - cycle_start = $to$ ; - return true; - } - } - color[vertex] = 2; - return false; -} - -int main() { - // чтение графа - - parent.assign(n, -1); - color.assign(n, 0); - cycle_start = -1; - for (int i = 0; i < n; ++i) - if (dfs(i)) - break; - - if (cycle_st == -1) - std::cout << "Acyclic" << "\n"; - else { - std::cout << "Cyclic" << "\n"; - std::vector cycle; - cycle.push_back(cycle_start); - for (int vertex = cycle_end; vertex != cycle_start; vertex = parent[vertex]) - cycle.push_back(vertex); - cycle.push_back(cycle_start); - reverse(cycle.begin(), cycle.end()); - for (size_t i = 0; i < cycle.size(); ++i) - std::cout << cycle[i]+1 << " "; - } -} -``` - -#### Топологическая сортировка - -*Условие:* -Дан ориентированный граф с n вершинами и m рёбрами. Требуется перенумеровать его вершины таким образом, чтобы каждое рёбро вело из вершины с меньшим номером в вершину с большим. Иными словами, требуется найти перестановку вершин (топологический порядок), соответствующую порядку, задаваемому всеми рёбрами графа. -(**Топологическая сортировка** - упорядочивание вершин ациклического ориентированного графа согласно частичному порядку, заданному ребрами орграфа на множестве его вершин.) -Топологическая сортировка может быть не единственной (например, если граф — пустой; или если есть три такие вершины `a`, `b`, `c`, что из `a` есть пути в `b` и в `c`, но ни из `b` в `c`, ни из `c` в `b` добраться нельзя). - -Топологической сортировки может не существовать вовсе — если граф содержит циклы (поскольку при этом возникает противоречие: есть путь и из одной вершины в другую, и наоборот). - -```c++ -#include - -int n; // число вершин -std::vector> graph; // граф -std::vector visited; -std::vector $to$ pological_sort; - -void dfs (int vertex) { - visited[vertex] = true; - for (int u : graph[vertex]) - if (!visited[u] == 0) - dfs(u); - $to$ pological_sort.push_back(vertex); -} - -void $to$ pological_sort() { - visited.assign(n, false); - answer.clear(); - for (int i = 0; i < n; ++i) - if (!visited[i]) - dfs(i); - reverse(answer.begin(), answer.end()); -} -``` - -### 1.4. Нахождение компонент сильной связности. Алгоритм Косарайю с доказательством корректности(концепция). Алгоритм Тарьяна без доказательства корректности - -#### Нахождение компонент сильной связности - -*Условие:* - -Дан ориентированный граф $G$, множество вершин которого $V$ и множество рёбер — $E$. Петли и кратные рёбра допускаются. Обозначим через $n$ количество вершин графа, через $m$ — количество рёбер. - -*Решение:* - -Напомним, что **компонентой сильной связности** (*strongly connected component*) называется такое (максимальное по включению) подмножество вершин $C4, что любые две вершины этого подмножества достижимы друг из друга, т.е. для любых $u, $v$ \in C: u \rightarrow v, $v$ \rightarrow u$, где символом $\rightarrow$ здесь и далее мы будем обозначать достижимость, т.е. существование пути из первой вершины во вторую. Понятно, что компоненты сильной связности для данного графа не пересекаются, т.е. фактически это разбиение всех вершин графа. Отсюда логично определение конденсации G как графа, получаемого из данного графа сжатием каждой компоненты сильной связности в одну вершину. Каждой вершине графа конденсации соответствует компонента сильной связности графа G, а ориентированное ребро между двумя вершинами $C_i$ и $C_j$ графа конденсации проводится, если найдётся пара вершин u из $C_i$, из $C_j$, между которыми существовало ребро в исходном графе, т.е. $(u, v)$ из $E$. - -Важнейшим свойством графа конденсации является то, что он ацикличен. Действительно, предположим, что $C \rightarrow C'$, докажем, что $C' \mapsto C$. Из определения конденсации получаем, что найдутся две вершины $u$ из $C$ и $v$ из $C'$, что $u \rightarrow v$. Доказывать будем от противного, т.е. предположим, что $C' \rightarrow C$, тогда найдутся две вершины $u'$ из $C$ и $v'$ из $C'$, что $v' \rightarrow u'$. Но т.к. $u$ и $u'$ находятся в одной компоненте сильной связности, то между ними есть путь; аналогично для $v$ и $v'$. В итоге, объединяя пути, получаем, что $v \rightarrow u$, и одновременно $u \rightarrow v$. Следовательно, $u$ и $v$ должны принадлежать одной компоненте сильной связности, т.е. получили противоречие, что и требовалось доказать - -#### Алгоритм Косарайю с доказательством корректности(концепция) - -Алгоритм Косарайю предназначен для поиска компонент сильной связности в ориентированном графе и состоит из трёх шагов: - -1. Выполнить поиск в глубину (DFS), пока не будут «помечены» все вершины. Вершина считается «помеченной», когда ей присвоено время выхода из рекурсии (см. основные понятия). - -2. Инвертировать исходный граф - -3. Выполнить DFS в порядке убывания пометок вершин. - -Полученные деревья каждого такта DFS последнего шага являются компонентами сильной связности - -**Доказательство корректности алгоритма:** - -Немного уточним, что требуется доказать: Вершины $u$ и $v$ сильно связаны $\Leftrightarrow$ после выполнения алгоритма они принадлежат одному дереву такта DFS. - -Если вершины u и $v$ были сильно связаны в графе $G$, на третьем этапе будет найден путь из одной вершины в другую (по Лемме (Инвертирование рёбер цикла не влияет на его цикличность)), так как на первом шаге был найден путь $u \rightarrow v$, а на третьем - путь $v \rightarrow u$. Это означает, что по окончании алгоритма обе вершины лежат в одном дереве. - -Вершины $u$ и $v$ лежат в одном и том же дереве поиска в глубину на третьем шаге алгоритма. Значит, они обе достижимы из корня $r$ этого дерева. - -Вершина $r$ была рассмотрена на 3 шаге раньше всех, значит время выхода из неё на 1 шаге больше, чем время выхода из вершин $u$ и $v$. Из этого мы получаем 2 случая: - -Обе эти вершины были достижимы из $r$ в исходном графе. Это означает сильную связность вершин $u$ и $r$ и сильную связность вершин $v$ и $r$ (по Лемме 3). Склеивая пути мы получаем связность вершин $u$ и $v$ (по Лемме 1) - -Хотя бы одна вершина не достижима из $r$ в исходном графе, например $v$. Значит и $r$ была не достижима из $v$ в исходном графе, так как время выхода из $r$ — больше (если бы она была достижима, то время выхода из $v$ было бы больше, чем из $r$, просмотрите ещё раз первый шаг примера). Значит между этими вершинами нет пути (ни в исходном, ни в инвертированном графах), но последнего быть не может, потому что по условию $v$ достижима из r на 3 шаге (в инвертированном графе) - -Значит, из случая 1 и не существования случая 2 получаем, что вершины $u$ и $v$ сильно связаны в обоих графах - -#### Алгоритм Тарьяна без доказательства корректности - -Основой для алгоритма является структура данных "Система непересекающихся множеств", которая и была изобретена Тарьяном (Tarjan). - -Алгоритм фактически представляет собой обход в глубину из корня дерева, в процессе которого постепенно находятся ответы на запросы. А именно, ответ на запрос $(v, u)$ находится, когда обход в глубину находится в вершине $u$, а вершина $v$ уже была посещена, или наоборот. - -Итак, пусть обход в глубину находится в вершине $v$ (и уже были выполнены переходы в её сыновей), и оказалось, что для какого-то запроса $(v, u)$ вершина u уже была посещена обходом в глубину. Научимся тогда находить $LCA$ этих двух вершин. - -Заметим, что $LCA(v, u)$ является либо самой вершиной $v$, либо одним из её предков. Получается, нам надо найти самую нижнюю вершину среди предков $v$ (включая её саму), для которой вершина u является потомком. Заметим, что при фиксированном $v$ по такому признаку (т.е. какой наименьший предок $v$ является и предком какой-то вершины) вершины дерева дерева распадаются на совокупность непересекающихся классов. Для каждого предка $p \neq v$ вершины $v$ её класс содержит саму эту вершину, а также все поддеревья с корнями в тех её сыновьях, которые лежат "слева" от пути до $v$ (т.е. которые были обработаны ранее, чем была достигнута $v$). - -Нам надо научиться эффективно поддерживать все эти классы, для чего мы и применим структуру данных "Система непересекающихся множеств". Каждому классу будет соответствовать в этой структуре множество, причём для представителя этого множества мы определим величину **ANCESTOR** — ту вершину $p$, которая и образует этот класс. - -Рассмотрим подробно реализацию обхода в глубину. Пусть мы стоим в некоторой вершине $v$. Поместим её в отдельный класс в структуре непересекающихся множеств, **ANCESTOR**[v] = $v$. Как обычно в обходе в глубину, перебираем все исходящие рёбра $(v, $to$ )$. Для каждого такого $to$ мы сначала должны вызвать обход в глубину из этой вершины, а потом добавить эту вершину со всем её поддеревом в класс вершины $v$. Это реализуется операцией **Union** структуры данных "система непересекающихся множеств", с последующей установкой **ANCESTOR** = $v$ для представителя множества (т.к. после объединения представитель класса мог измениться). Наконец, после обработки всех рёбер мы перебираем все запросы вида $(v, u)$, и если u была помечена как посещённая обходом в глубину, то ответом на этот запрос будет вершина $LCA(v,u)$ = **ANCESTOR**[FindSet(u)]. Нетрудно заметить, что для каждого запроса это условие (что одна вершина запроса является текущей, а другая была посещена ранее) выполнится ровно один раз. - -Оценим асимптотику. Она складывается из нескольких частей. Во-первых, это асимптотика обхода в глубину, которая в данном случае составляет $O(n)$. Во-вторых, это операции по объединению множеств, которые в сумме для всех разумных n затрачивают $O(n)$ операций. В-третьих, это для каждого запроса проверка условия (два раза на запрос) и определение результата (один раз на запрос), каждое, опять же, для всех разумных n выполняется за $O(1)$. Итоговая асимптотика получается $O(n + m)$, что означает для достаточно больших $m$ $(n = O(m))$ ответ за $O(1)$ на один запрос - -### 1.5. Компоненты реберной двусвязности. Мосты. Поиск мостов - -#### Компоненты реберной двусвязности - -> **Компонентами рёберной двусвязности** (англ. *costal doubly-linked components*) графа называют его подграфы, множества вершин которых - классы эквивалентности рёберной двусвязности, а множества рёбер - множества ребер из соответствующих классов эквивалентности. - -#### Мосты - -> **Мостом** называется такое ребро, удаление которого делает граф несвязным (или, точнее, увеличивает число компонент связности). - -#### Поиск мостов - -- Алгоритм: - Запустим обход в глубину из произвольной вершины графа; обозначим её через **root**. Заметим следующий факт (который несложно доказать): - - Пусть мы находимся в обходе в глубину, просматривая сейчас все рёбра из вершины $v$. Тогда, если текущее ребро $(v,to)$ таково, что из вершины $to$ и из любого её потомка в дереве обхода в глубину нет обратного ребра в вершину $v$ или какого-либо её предка, то это ребро является мостом. В противном случае оно мостом не является. (В самом деле, мы этим условием проверяем, нет ли другого пути из $v$ в $to$ , кроме как спуск по ребру $(v,to)$ дерева обхода в глубину.) - Теперь осталось научиться проверять этот факт для каждой вершины эффективно. Для этого воспользуемся "временами входа в вершину", вычисляемыми алгоритмом поиска в глубину. - - Итак, пусть $tin[v]$ — это время захода поиска в глубину в вершину $v$. Теперь введём массив $fup[v]$, который и позволит нам отвечать на вышеописанные запросы. Время $fup[v]$ равно минимуму из времени захода в саму вершину $tin[v]$, времён захода в каждую вершину $p$, являющуюся концом некоторого обратного ребра $(v, p)$, а также из всех значений $fup[to]$ для каждой вершины $to$ , являющейся непосредственным сыном $v$ в дереве поиска: - - $fup[v] = \min(tin[v], tin[p], fup(to))$ $(v, p)$ - back edge, $(v, to)$ - tree edge (здесь "back edge" — обратное ребро, "tree edge" — ребро дерева) - - Тогда, из вершины $v$ или её потомка есть обратное ребро в её предка тогда и только тогда, когда найдётся такой сын $to$ , что $fup[to] \leq tin[v]$. (Если $fup[to] = tin[v]$, то это означает, что найдётся обратное ребро, приходящее точно в $v$; если же $fup[to] < tin[v]$, то это означает наличие обратного ребра в какого-либо предка вершины $v$.) - - Таким образом, если для текущего ребра $(v,to)$ (принадлежащего дереву поиска) выполняется $fup[to] > tin[v]$, то это ребро является мостом; в противном случае оно мостом не является. - -### 1.6. Компоненты вершинной двусвязности. Точки сочленения. Поиск точек сочленения - -#### Компоненты вершинной двусвязности - -> **Компонентами вершинной двусвязности** графа, называют его подграфы, множества ребер которых — классы эквивалентности вершинной двусвязности, а множества вершин — множества всевозможных концов ребер из соответствующих классов. - -#### Точки сочленения - -> **Точкой сочленения** называется вершина, при удалении которой связный неориентированный граф становится несвязным. - -#### Поиск точек сочленения - -- Алгоритм: - Запустим обход в глубину из произвольной вершины графа; обозначим её через $root$. Заметим следующий факт (который несложно доказать): - - Пусть мы находимся в обходе в глубину, просматривая сейчас все рёбра из вершины $v \neq root$. Тогда, если текущее ребро $(v, to)$ таково, что из вершины $to$ и из любого её потомка в дереве обхода в глубину нет обратного ребра в какого-либо предка вершины v, то вершина $v$ является точкой сочленения. В противном случае, т.е. если обход в глубину просмотрел все рёбра из вершины $v$, и не нашёл удовлетворяющего вышеописанным условиям ребра, то вершина $v$ не является точкой сочленения. (В самом деле, мы этим условием проверяем, нет ли другого пути из $v$ в $to$ ) - Рассмотрим теперь оставшийся случай: $v = root$. Тогда эта вершина является точкой сочленения тогда и только тогда, когда эта вершина имеет более одного сына в дереве обхода в глубину. (В самом деле, это означает, что, пройдя из $root$ по произвольному ребру, мы не смогли обойти весь граф, откуда сразу следует, что $root$ — точка сочленения). - (Ср. формулировку этого критерия с формулировкой критерия для алгоритма поиска мостов.) - - Теперь осталось научиться проверять этот факт для каждой вершины эффективно. Для этого воспользуемся "временами входа в вершину", вычисляемыми алгоритмом поиска в глубину. - - Итак, пусть $tin[v]$ — это время захода поиска в глубину в вершину v. Теперь введём массив $fup[v]$, который и позволит нам отвечать на вышеописанные запросы. Время $fup[v]$ равно минимуму из времени захода в саму вершину $tin[v]$, времён захода в каждую вершину p, являющуюся концом некоторого обратного ребра $(v, p)$, а также из всех значений $fup[to]$ для каждой вершины $to$ , являющейся непосредственным сыном $v$ в дереве поиска: - - $fup[v] = \min(tin[v], tin[p], fup(to))$ $(v, p)$ - back edge, $(v, to)$ - tree edge (здесь "back edge" — обратное ребро, "tree edge" — ребро дерева) - - Тогда, из вершины $v$ или её потомка есть обратное ребро в её предка тогда и только тогда, когда найдётся такой сын $to$ , что $fup[to] < tin[v]$. - - Таким образом, если для текущего ребра $(v,to)$ (принадлежащего дереву поиска) выполняется $fup[to] \geq tin[v]$, то вершина $v$ является точкой сочленения. Для начальной вершины $v = root$ критерий другой: для этой вершины надо посчитать число непосредственных сыновей в дереве обхода в глубину. - -### 1.7. Волновой алгоритм. Обход в ширину (применение очереди в волновом алгоритме) - -#### Волновой алгоритм - -> **Алгоритм волновой трассировки** (*волновой алгоритм*) — алгоритм поиска пути, алгоритм поиска кратчайшего пути на планарном графе. Принадлежит к алгоритмам, основанным на методах поиска в ширину. - -Алгоритм предназначен для поиска кратчайшего пути от стартовой ячейки к конечной ячейке, если это возможно, либо, при отсутствии пути, выдать сообщение о непроходимости. - -Работа алгоритма включает в себя три этапа: инициализацию, распространение волны и восстановление пути. - -Инициализация - -```c -Пометить стартовую ячейку -d := 0 -``` - -Распространение волны - -```c -ЦИКЛ - ДЛЯ каждой ячейки loc, помеченной числом d - пометить все соседние свободные непомеченные ячейки числом d + 1 - КЦ - d := d + 1 -ПОКА (финишная ячейка не помечена) И (есть возможность распространения волны) -``` - -Восстановление пути - -```c -ЕСЛИ финишная ячейка помечена -ТО - перейти в финишную ячейку - ЦИКЛ - выбрать среди соседних ячейку, помеченную числом на 1 меньше числа в текущей ячейке - перейти в выбранную ячейку и добавить её к пути - ПОКА текущая ячейка — не стартовая - ВОЗВРАТ путь найден -ИНАЧЕ - ВОЗВРАТ путь не найден -``` - -#### Обход в ширину (применение очереди в волновом алгоритме) - -> **Поиск в ширину** (англ. *breadth-first search*) — один из основных алгоритмов на графах, позволяющий находить все кратчайшие пути от заданной вершины и решать многие другие задачи. - -- Описание алгоритма - На вход алгоритма подаётся невзвешенный граф и номер стартовой вершины $s$. Граф может быть как ориентированным, так и неориентированным — для алгоритма это не важно. - - Основную идею алгоритма можно понимать как процесс «поджигания» графа: на нулевом шаге мы поджигаем вершину $s$, а на каждом следующем шаге огонь с каждой уже горящей вершины перекидывается на всех её соседей, в конечном счете поджигая весь граф. - - Если моделировать этот процесс, то за каждую итерацию алгоритма будет происходить расширение «кольца огня» в ширину на единицу. Номер шага, на котором вершина $v$ начинает гореть, в точности равен длине её минимального пути из вершины s. - -```c++ -std::vector graph; - -void bfs(int s) { - std::queue queue; - queue.push(s); - - std::vector depth(n, -1), parent(n); - depth[s] = 0; - - while (!queue.empty()) { - int vertex = queue.front(); - queue.pop(); - for (int u : g[vertex]) { - if (depth[u] == -1) { - queue.push(u); - depth[u] = depth[vertex] + 1; - parent[u] = vertex; - } - } - } -} -``` - -Теперь, чтобы восстановить кратчайший путь до какой-то вершины v, это можно сделать через массив p: - -```c++ -while (v != s) { - std::cout << v << endl; - v = p[v]; -} -``` - -Обратим внимание, что путь выведется в обратном порядке. - -### 1.8. Критерий существования Эйлерова пути и цикла в ориентированном и неориентированном графе. Поиск эйлерова пути и цикла - -#### Критерий существования Эйлерова пути и цикла в неориентированном графе - -- Согласно теореме, доказанной Эйлером, эйлеров цикл существует тогда и только тогда, когда - 1. Граф связный или будет являться связным, если удалить из него все изолированные вершины. - 2. В нём отсутствуют вершины нечётной степени. - -- Эйлеров путь в графе существует тогда и только тогда, когда - 1. Граф связный - 2. Содержит не более двух вершин нечётной степени. Ввиду леммы о рукопожатиях, число вершин с нечётной степенью должно быть чётным. А значит эйлеров путь существует только тогда, когда это число равно нулю или двум. Причём, когда оно равно нулю, эйлеров путь вырождается в эйлеров цикл. - -#### Критерий существования Эйлерова пути и цикла в ориентированном графе - -- В ориентированном графе $G = (V, E)$ существует эйлеров цикл тогда и только тогда, когда: - 1. Входная степень любой вершины равна ее выходной степени. - 2. Все компоненты слабой связности кроме, может быть одной, не содержат ребер. -- В ориентированном графе $G = (V, E)$ существует эйлеров путь если: - 1. Входная степень любой вершины равна ее выходной степени, кроме двух вершин графа, для одной из которых (deg+) − (deg−) = 1, а для другой (deg+) − (deg−) = −1 . - 2. Все компоненты слабой связности кроме, может быть одной, не содержат ребер. - -#### Поиск эйлерова пути и цикла - -- Поиск эйлерова пути в графе - Можно всегда свести задачу поиска эйлерова пути к задаче поиска эйлерова цикла. Действительно, предположим, что эйлерова цикла не существует, а эйлеров путь существует. Тогда в графе будет ровно 2 вершины нечётной степени. Соединим эти вершины ребром, и получим граф, в котором все вершины чётной степени, и эйлеров цикл в нём существует. Найдём в этом графе эйлеров цикл (алгоритмом, описанным ниже), а затем удалим из ответа несуществующее ребро. - -- Поиск эйлерова цикла в графе - Будем рассматривать самый общий случай — случай ориентированного мультиграфа, возможно, с петлями. Также мы предполагаем, что эйлеров цикл в графе существует (и состоит хотя бы из одной вершины). Для поиска эйлерова цикла воспользуемся тем, что эйлеров цикл — это объединение всех простых циклов графа. Следовательно, наша задача — эффективно найти все циклы и эффективно объединить их в один. - - Реализовать это можно, например, так, рекурсивно: - - ```c - procedure find_all_cycles (v) - var массив cycles - 1. пока есть цикл, проходящий через v, находим его - добавляем все вершины найденного цикла в массив cycles (сохраняя порядок обхода) - удаляем цикл из графа - 2. идем по элементам массива cycles - каждый элемент cycles[i] добавляем к ответу - из каждого элемента рекурсивно вызываем себя: find_all_cycles (cycles[i]) - ``` - - Достаточно вызвать эту процедуру из любой вершины графа, и она найдёт все циклы в графе, удалит их из графа и объединит их в один эйлеров цикл. - Для поиска цикла на шаге 1 используем поиск в глубину. - Сложность полученного алгоритма — $O(|E|)$, то есть линейная относительно количества рёбер в данном графе - -
- -## 2. Планарность графа - -### 2.1. Формула Эйлера. Теорема Портнягина-Куратовского. Теорема Вагнера - -#### Формула Эйлера - -Для произвольного плоского связного графа $G$ с $V$ вершинами, $E$ ребрами и $F$ гранями справедливо следующее соотношение: $V − E + F = 2$. - -#### Теорема Портнягина-Куратовского - -Теорема Понтрягина — Куратовского, или теорема Куратовского, — теорема в теории графов, дающая необходимое и достаточное условие планарности графа. Имеет эквивалентную формулировку на языке миноров, так называемою теорему Вагнера. - -Теорема утверждает, что графы $K_5$ (полный граф на 5 вершинах) и $K_{3,3}$ (полный двудольный граф имеющий по 3 вершины в каждой доле) являются единственными минимальными непланарными графами. - -#### Теорема Вагнера - -Теорема Вагнера — характеризация планарных графов тесно связанная с теоремой Понтрягина — Куратовского. - -Теорема утверждает, что конечный граф является планарным тогда и только тогда, когда его миноры не включают ни $K_5$ (полный граф с пятью вершинами), ни $K_{3,3}$ (коммунальный граф, полный двудольный граф с тремя вершинами в каждой доле). - -### 2.2. Гамма алгоритм. Контактная вершина - -#### Гамма алгоритм - -- Чтобы проверить планарность графа и произвести его плоскую укладку, удобно пользоваться гамма-алгоритмом. -- Определения: - 1. Сегмент графа $Г$ относительно подграфа $H$ — компонента связности графа $G \setminus H$. - 2. *Допустимая грань сегмента* — грань графа $H$, содержащая все контактные вершины. - 3. **Контактная вершина** сегмента $S$ графа $G$ подграфа $H$ — любая вершина в $S$ и в $H$. - 4. *Гамма-цепь сегмента* — любая цепь без повторов вершин, содержащих ровно две контактные вершины — начало и конец. -- Алгоритм: - 1. Уложить на плоскость любой цикл $H$ графа $G$ без повторов вершин. - 2. Построить все сегменты $S_1$ , $...$ , $S_k$ графа $G$ по $H$. - 3. Если есть сегмент $S_i$ c одной допустимой гранью — выбрать его. - 4. Если все сегменты имеют несколько дополнительных граней — выбрать любой. - 5. Выбрать произвольную гамма-цепь сегмента и уложить её в допустимую грань. - 6. Перейти к шагу (2), добавив гамма-цепь к графу $H$. - -### 2.3. Теорема о корректности Гамма алгоритма. Асимптотика алгоритма - -#### Теорема о корректности Гамма алгоритма - -- Теорема: Гамма-алгоритм корректен, то есть если $G$ — планарный граф, то результатом каждого шага гамма-алгоритма является частичная укладка $G'$. -- Доказательство: - Докажем индукцией по числу шагов. - - База индукции: полученный на этапе инициализации граф $G_0$ является простым циклом, он будет присутствовать в любой укладке графа $G$. Таким образом, $G_0$ является частичной укладкой. - - Шаг индукции: пусть граф $G_{k−1}$, полученный на $k−1$-м шаге работы алгоритма, является частичной укладкой. Докажем, что граф $G_k = G_{k−1} \cup L_k$, полученный на $k$-м шаге присоединением цепи $L_k$, также является частичной укладкой. - - Заметим, что на текущем шаге нет такого сегмента $S$ относительно $G_{k−1}$, для которого бы выполнялось равенство $Γ(S) = \varnothing$, так как в противном случае существовала бы цепь этого сегмента, контактные вершины которой принадлежали бы разным граням и укладка которой была бы невозможна. Следовательно, нельзя было бы уложить $S$,что противоречит тому, что $G$ — планарный граф. Значит, мы можем рассматривать только следующие два случая: - 1. Существует сегмент $S$ для которого есть единственная вмещающая грань $Γ$, то есть $|Γ(S)| = 1$. Так как только грани $Γ$ принадлежат все контактные вершины $S$, то укладка этого сегмента в эту грань неизбежна. Это значит, что помещая любую цепь $L \subset S$, снова получим частичную укладку графа. - - 2. Для любого сегмента $S$ $|Γ(S)| \geq 2$. Построим граф $A(G'k−1)$, который по лемме 2 является двудольным. Рассмотрим его связную компоненту $K$, которая содержит не менее двух вершин. Граф $K$ также является двудольным. По лемме 1 для любого сегмента $S \in K$ справедливо $Γ(S)={Γ1, Γ2}$. Так как граф $K$ двудольный, то мы можем по очереди помещать сегменты $K$ в разные грани, причем конфликтующих сегментов не возникнет в силу четности всех циклов в графе. Результатом будет частичная укладка графа. - - Таким образом, на каждом шаге мы получаем частичную укладку графа, что доказывает корректность гамма-алгоритма. - -**Асимптотика алгоритма - $O(n^3)$.** - -
- -## 3. Кратчайшие пути во взвешенном графе - -### 3.1. Алгоритм Дейкстры. Доказательство корректности(с доказательством). Оценка времени работы. Дерево кратчайших путей - -#### Алгоритм Дейкстры - -**Алгоритм Дейкстры** (англ. *Dijkstra’s algorithm*) — алгоритм на графах, изобретённый нидерландским учёным Эдсгером Дейкстрой в 1959 году. Находит кратчайшие пути от одной из вершин графа до всех остальных. Алгоритм работает только для графов без рёбер отрицательного веса. - -```c -func dijkstra(s): - for $v \in V$ - d[v] = $ \infinity $ - used[v] = false - d[s] = 0 - for $i \in V$ - v = null - for $j \in V$ // найдём вершину с минимальным расстоянием - if !used[j] and (v == null or d[j] < d[v]) - v = j - if d[v] == $\infinity$ - break - used[v] = true - for e : исходящие из v рёбра // произведём релаксацию по всем рёбрам, исходящим из v - if d[v] + e.len < d[e.to] - d[e.to] = d[v] + e.len -``` - -#### Доказательство корректности - -- Пусть $G=(V,E)$ — ориентированный взвешенный граф, вес рёбер которого неотрицателен, s — стартовая вершина. Тогда после выполнения алгоритма Дейкстры $d(u)=\rho(s, u)$ для всех $u$, где $\rho (s,u)$ — длина кратчайшего пути из вершины $s$ в вершину $u$. - -Докажем по индукции, что в момент посещения любой вершины $u$, $d(u)= \rho (s,u)$. - -На первом шаге выбирается $s$, для неё выполнено: $d(s) = \rho (s,s) = 0$. Пусть для $n$ первых шагов алгоритм сработал верно и на $n+1$ шагу выбрана вершина $u$. Докажем, что в этот момент $d(u)= \rho (s,u)$. Для начала отметим, что для любой вершины $v$, всегда выполняется $d(v) \geq \rho (s,v)$ (алгоритм не может найти путь короче, чем кратчайший из всех существующих). Пусть $P$ — кратчайший путь из $s$ в $u$, $v$ — первая непосещённая вершина на $P$, $z$ — предшествующая ей (следовательно, посещённая). Поскольку путь $P$ кратчайший, его часть, ведущая из $s$ через $z$ в $v$, тоже кратчайшая, следовательно $\rho(s,v)= \rho(s,z)+w(zv)$. По предположению индукции, в момент посещения вершины $z$ выполнялось $d(z)= \rho (s,z)$, следовательно, вершина $v$ тогда получила метку не больше чем $d(z) + w(zv) = \rho (s,z) + w(zv) = \rho(s,v)$, следовательно, $d(v)=\rho(s,v)$. С другой стороны, поскольку сейчас мы выбрали вершину $u$, её метка минимальна среди непосещённых, то есть $d(u) \leq d(v) = \rho (s,v) \leq \rho (s,u)$, где второе неравенсто верно из-за ранее упомянутого определения вершины $v$ в качестве первой непосещённой вершины на $P$, то есть вес пути до промежуточной вершины не превосходит веса пути до конечной вершины вследствие неотрицательности весовой функции. Комбинируя это с $d(u) \geq \rho(s,u)$, имеем $d(u)=\rho(s,u)$, что и требовалось доказать. Поскольку алгоритм заканчивает работу, когда все вершины посещены, в этот момент $d(u)= \rho (s,u)$ для всех $u$. - -#### Оценка времени работы - -- Если $m = n ^ 2$(граф плотный), то $O(n^2)$. -- Если $m = n$(граф разреженный), то $O(m log n)$. - -#### Дерево кратчайших путей - -Решение ряда прикладных задач сводится к нахождению дерева кратчайших путей с корнем в заданной вершине $s$. Такое дерево дает кратчайшие $(s, v)$-пути от заданной вершины $s$ до любой вершины $v$ орграфа $G = (V, Е)$. Если веса всех дуг орграфа $G$ неотрицательны, то дерево кратчайших путей можно построить с помощью алгоритма Дейкстры. - -### 3.2. Потенциалы. Условие применимости алгоритма Дейкстры для измененных длин ребер. Потенциал $\pi(v) = \rho(v, t)$ - -#### Потенциалы - -Пусть дана транспортная сеть $G(V,E)$, где $V$ — множество вершин графа, а $E$ — множество рёбер. Введем в каждой вершине потенциал $\pi(v)$. Тогда потенциальный вес (то есть стоимость) ребра $(u,v)$ определяется как $w_p(u,v)=w(u,v)+\pi(u)−\pi(v)$ - -#### Условие применимости алгоритма Дейкстры для измененных длин ребер - -Можно применять, когда нет отрицательных циклов (следует из курса дискретной математики 1 курс 2 семестр) - -#### Потенциал $\pi(v) = \rho(v, t)$ - -### 3.3. Алгоритм A*. Условие монотонности на эвристику. Примеры эвристик - -#### Алгоритм A* - -Алгоритм A* - алгоритм поиска по первому наилучшему совпадению на графе, который находит маршрут с наименьшей стоимостью от одной вершины (начальной) к другой (целевой, конечной). - -Порядок обхода вершин определяется эвристической функцией «расстояние + стоимость» (обычно обозначаемой как $f(x)$). Эта функция — сумма двух других: функции стоимости достижения рассматриваемой вершины (x) из начальной (обычно обозначается как $g(x)$ и может быть как эвристической, так и нет), и функции эвристической оценки расстояния от рассматриваемой вершины к конечной (обозначается как $h(x)$). - -1. Функция $h(x)$ должна быть *допустимой эвристической оценкой*, то есть не должна переоценивать расстояния к целевой вершине. Например, для задачи маршрутизации $h(x)$ может представлять собой расстояние до цели по прямой линии, так как это физически наименьшее возможное расстояние между двумя точками. -2. Более сильное условие — функция $h(v)$ должна быть **монотонной**. (Эвристическая функция $h(v)$ называетя **монотонной** (или преемственной), если для любой вершины $v_1$ и ее потомка $v_2$ разность $h(v_1)$ и $h(v_2)$ не превышает фактического веса ребра $c(v_1, v_2)$ от $v_1$ до $v_2$, а эвристическая оценка целевого состояния равна нулю.) - -```c -function A*(start, goal, f) - % множество уже пройденных вершин - var closed := the empty set - % множество частных решений - var open := make_queue(f) - enqueue(open, path(start)) - while open is not empty - var p := remove_first(open) - var x := the last node of p - if x in closed - continue - if x = goal - return $\rho$ - add(closed, x) - % добавляем смежные вершины - foreach y in successors(x) - enqueue(open, add_to_path(p, y)) - return failure -``` - -#### Примеры эвристик - -- Если мы можем перемещаться в четырех направлениях, то в качестве эвристики стоит выбрать манхэттенское расстояние $h(v)=|v.x−goal.x|+|v.y−goal.y|$. -- Расстояние Чебышева применяется, когда к четырем направлениям добавляются диагонали: $h(v)=max(|v.x−goal.x|,|v.y−goal.y|)$. -- Если передвижение не ограничено сеткой, то можно использовать евклидово расстояние по прямой: $h(v)=((v.x−goal.x) ^ 2+(v.y−goal.y)^2) ^ {1/2}$. - -### 3.4. Алгоритм Форда-Беллмана. Хранение в матрице: $D_{vk}$ равно длине кратчайшего пути до вершины $v$ за ровно $k$ ребер (не более $k$ ребер). Доказательство корректности (полное). Оценка времени работы - -Количество путей длины k рёбер можно найти с помощью метода динамического программирования. Пусть $d[k][u]$ — количество путей длины k рёбер, заканчивающихся в вершине u. Тогда $d[k][u]= \Sigma v: v u \in E(d[k−1][v])$. - -Аналогично посчитаем пути кратчайшей длины. Пусть $s$ — стартовая вершина. Тогда $d[k][u]=\min v: v u \in E(d[k−1][v]+w(u,v))$, при этом $d[0][s]=0$, а $d[0][u]=+\infty$ - -Если существует кратчайший путь от s до t, то $\rho(s,t)=\min d[k][t](k=0..n−1)$ - -Пусть кратчайший путь состоит из $k$ ребер, тогда корректность формулы следует из динамики, приведенной ниже. - -```c++ -for k = 0 to |V|−2 // вершины нумеруются с единицы - for v∈V - for (u,v)∈E - d[k + 1][v] = min(d[k + 1][v], d[k][u] + ω(u,v)) // ω(u,v) — вес ребра uv -``` - - -```c++ -bool fordBellman(s): - for v∈V - d[v] = 1 - d[s] = 0 - for i = 0 to |V|−1 - for (u,v)∈E - if d[v] > d[u] + ω(u,v) // ω(u,v) — вес ребра uv - d[v] = d[u] + ω(u,v) - for (u,v)∈E - if d[v] > d[u] + ω(u,v) - return false - return true -``` - -#### Доказательство корректности(полное) - -Во-первых, сразу заметим, что для недостижимых из $v$ вершин алгоритм отработает корректно: для них метка $d[]$ так и останется равной бесконечности (т.к. алгоритм Форда-Беллмана найдёт какие-то пути до всех достижимых из $s$ вершин, а релаксация во всех остальных вершинах не произойдёт ни разу). - -Докажем теперь следующее утверждение: после выполнения $i$ фаз алгоритм Форда-Беллмана корректно находит все кратчайшие пути, длина которых (по числу рёбер) не превосходит $i$. - -Иными словами, для любой вершины $a$ обозначим через $k$ число рёбер в кратчайшем пути до неё (если таких путей несколько, можно взять любой). Тогда это утверждение говорит о том, что после $k$ фаз этот кратчайший путь будет найден гарантированно. - -**Доказательство.** - -Рассмотрим произвольную вершину a, до которой существует путь из стартовой вершины v, и рассмотрим кратчайший путь до неё: $(p_0=v, p_1, ..., p_k=a)$. Перед первой фазой кратчайший путь до вершины $p_0=v$ найден корректно. Во время первой фазы ребро $(p_0,p_1)$ было просмотрено алгоритмом Форда-Беллмана, следовательно, расстояние до вершины $p_1$ было корректно посчитано после первой фазы. Повторяя эти утверждения k раз, получаем, что после $k$-й фазы расстояние до вершины $p_k=a$ посчитано корректно, что и требовалось доказать. - -Последнее, что надо заметить — это то, что любой кратчайший путь не может иметь более $n-1$ ребра. Следовательно, алгоритму достаточно произвести только $n-1$ фазу. После этого ни одна релаксация гарантированно не может завершиться улучшением расстояния до какой-то вершины. - -#### Оценка времени работы - -Инициализация занимает $\theta(V)$ времени, каждый из $|V|−1$ проходов требует $\theta(E)$ времени, обход по всем ребрам для проверки наличия отрицательного цикла занимает $O(E)$ времени. Значит алгоритм Беллмана-Форда работает за $O(VE)$ времени. - -### 3.5. Восстановление пути. Детектирование цикла отрицательного веса. Поиск самого цикла - -#### Восстановление пути - -```c++ -void solve() { - vector d (n, INF); - d[v] = 0; - vector p (n, -1); - for (;;) { - bool any = false; - for (int j=0; j d[e[j].a] + e[j].cost) { - d[e[j].b] = d[e[j].a] + e[j].cost; - p[e[j].b] = e[j].a; - any = true; - } - if (!any) break; - } - - if (d[t] == INF) - cout << "No path from " << v << " to " << t << "."; - else { - vector path; - for (int cur=t; cur!=-1; cur=p[cur]) - path.push_back (cur); - reverse (path.begin(), path.end()); - - cout << "Path from " << v << " to " << t << ": "; - for (size_t i=0; i **Алгоритм Флойда** (*алгоритм Флойда–Уоршелла*) — алгоритм нахождения длин кратчайших путей между всеми парами вершин во взвешенном ориентированном графе. Работает корректно, если в графе нет циклов отрицательной величины, а в случае, когда такой цикл есть, позволяет найти хотя бы один такой цикл. - -Ключевая идея алгоритма — разбиение процесса поиска кратчайших путей на фазы. - -Перед $k$-ой фазой $(k = 1 ... n)$ считается, что в матрице расстояний $d[][]$ сохранены длины таких кратчайших путей, которые содержат в качестве внутренних вершин только вершины из множества ${1, 2, ..., k-1}$ (вершины графа мы нумеруем, начиная с единицы). - -Иными словами, перед $k$-ой фазой величина $d[i][j]$ равна длине кратчайшего пути из вершины $i$ в вершину $j$, если этому пути разрешается заходить только в вершины с номерами, меньшими k (начало и конец пути не считаются). - -Легко убедиться, что чтобы это свойство выполнилось для первой фазы, достаточно в матрицу расстояний $d[][]$ записать матрицу смежности графа: $d[i][j] = g[i][j]$ — стоимости ребра из вершины $i$ в вершину $j$. При этом, если между какими-то вершинами ребра нет, то записать следует величину "бесконечность". Из вершины в саму себя всегда следует записывать величину 0, это критично для алгоритма. - -Пусть теперь мы находимся на $k$-ой фазе, и хотим пересчитать матрицу $d[][]$ таким образом, чтобы она соответствовала требованиям уже для $k+1$-ой фазы. Зафиксируем какие-то вершины $i$ и $j$. У нас возникает два принципиально разных случая: - -Кратчайший путь из вершины $i$ в вершину $j$, которому разрешено дополнительно проходить через вершины ${1, 2, ..., k}$, совпадает с кратчайшим путём, которому разрешено проходить через вершины множества ${1, 2, ..., k-1}$. -В этом случае величина $d[i][j]$ не изменится при переходе с $k$-ой на $k+1$-ую фазу. - -"Новый" кратчайший путь стал лучше "старого" пути. Это означает, что "новый" кратчайший путь проходит через вершину $k$. Сразу отметим, что мы не потеряем общности, рассматривая далее только простые пути (т.е. пути, не проходящие по какой-то вершине дважды). - -Тогда заметим, что если мы разобьём этот "новый" путь вершиной $k$ на две половинки (одна идущая $i \rightarrow k$, а другая — $k \rightarrow j$), то каждая из этих половинок уже не заходит в вершину $k$. Но тогда получается, что длина каждой из этих половинок была посчитана ещё на $k-1$-ой фазе или ещё раньше, и нам достаточно взять просто сумму $d[i][k] + d[k][j]$, она и даст длину "нового" кратчайшего пути. - -Объединяя эти два случая, получаем, что на $k$-ой фазе требуется пересчитать длины кратчайших путей между всеми парами вершин $i$ и $j$ следующим образом: - -```c++ -new_d[i][j] = min(d[i][j], d[i][k] + d[k][j]); -``` - -Таким образом, вся работа, которую требуется произвести на $k$-ой фазе — это перебрать все пары вершин и пересчитать длину кратчайшего пути между ними. В результате после выполнения $n$-ой фазы в матрице расстояний $d[i][j]$ будет записана длина кратчайшего пути между $i$ и $j$, либо бесконечность, если пути между этими вершинами не существует. - -Последнее замечание, которое следует сделать, — то, что можно не создавать отдельную матрицу $new_d[][]$ для временной матрицы кратчайших путей на $k$-ой фазе: все изменения можно делать сразу в матрице $d[][]$. В самом деле, если мы улучшили (уменьшили) какое-то значение в матрице расстояний, мы не могли ухудшить тем самым длину кратчайшего пути для каких-то других пар вершин, обработанных позднее. - -#### Доказательство (концепция) - -Доказательство "правильности" работы этого алгоритма также очевидно и выполняется с помощью математической индукции по $k$, показывая, что на $k$-й итерации вершина $k$ включается в путь только тогда, когда новый путь короче старого. - -#### Восстановление пути - -Легко поддерживать дополнительную информацию — так называемых "предков", по которым можно будет восстанавливать сам кратчайший путь между любыми двумя заданными вершинами в виде последовательности вершин. - -Для этого достаточно кроме матрицы расстояний $d[][]$ поддерживать также матрицу предков $p[][]$, которая для каждой пары вершин будет содержать номер фазы, на которой было получено кратчайшее расстояние между ними. Понятно, что этот номер фазы является не чем иным, как "средней" вершиной искомого кратчайшего пути, и теперь нам просто надо найти кратчайший путь между вершинами $i$ и $p[i][j]$, а также между $p[i][j]$ и $j$. Отсюда получается простой рекурсивный алгоритм восстановления кратчайшего пути. - -### 3.8. Нахождение цикла отрицательного веса - -Если в графе есть циклы отрицательного веса, то формально алгоритм Флойда-Уоршелла неприменим к такому графу. - -На самом же деле, для тех пар вершин $i$ и $j$, между которыми нельзя зайти в цикл отрицательного вес, алгоритм отработает корректно. - -Для тех же пар вершин, ответа для которых не существует (по причине наличия отрицательного цикла на пути между ними), алгоритм Флойда найдёт в качестве ответа какое-то число (возможно, сильно отрицательное, но не обязательно). Тем не менее, можно улучшить алгоритм Флойда, чтобы он аккуратно обрабатывал такие пары вершин и выводил для них, например, - бесконечность. - -Для этого можно сделать, например, следующий критерий "не существования пути". Итак, пусть на данном графе отработал обычный алгоритм Флойда. Тогда между вершинами $i$ и $j$ не существует кратчайшего пути тогда и только тогда, когда найдётся такая вершина $t$, достижимая из $i$ и из которой достижима $j$, для которой выполняется $d[t][t] < 0$. - -Кроме того, при использовании алгоритма Флойда для графов с отрицательными циклами следует помнить, что возникающие в процессе работы расстояния могут сильно уходить в минус, экспоненциально с каждой фазой. Поэтому следует принять меры против целочисленного переполнения, ограничив все расстояния снизу какой-нибудь величиной (например, - Бесконечность). - -### 3.9. Алгоритм Джонсона. Добавление фиктивного корня и фиктивных ребер для запуска алгоритма Форда-Беллмана - -#### Алгоритм Джонсона - -> Алгоритм Джонсона позволяет найти кратчайшие пути между всеми парами вершин в течение времени $O(V \cdot 2 \log(V) + VE)$. Для разреженных графов этот алгоритм ведет себя асимптотически быстрее алгоритма Флойда. Этот алгоритм либо возвращает матрицу кратчайших расстояний между всеми парами вершин, либо сообщение о том, что в графе существует цикл отрицательной длины. - -В этом алгоритме используется метод изменения веса (англ. reweighting). Суть его заключается в том, что для заданного графа G строится новая весовая функция $\omega_\varphi$, неотрицательная для всех ребер графа G и сохраняющая кратчайшие пути. Такая весовая функция строится с помощью так называемой потенциальной функции. - -Пусть $\varphi: V \rightarrow R$ — произвольное отображение из множества вершин в вещественные числа. Тогда новой весовой функцией будет $\omega_\varphi(u,v)=\varphi(u,v)+\varphi(u)−\varphi(v)$. - -Такая потенциальная функция строится добавлем фиктивной вершины $s$ в $G$, из которой проведены ориентированные ребра нулевого веса во все остальные вершины графа, и запуском алгоритма Форда-Беллмана из нее ($\varphi(v)$ будет равно длине кратчайшего пути из $s$ в $v$). На этом же этапе мы сможем обнаружить наличие отрицательного цикла в графе. - -Теперь, когда мы знаем, что веса всех ребер неотрицательны, и кратчайшие пути сохранятся, можно запустить алгоритм Дейкстры из каждой вершины и таким образом найти кратчайшие расстояния между всеми парами вершин. - -#### Добавление фиктивного корня и фиктивных ребер для запуска алгоритма Форда-Беллмана - -Как в случае применения алгоритма Беллмана-Форда для обнаружения отрицательных циклов, существуют два способа сделать вес каждого ребра неотрицательным в произвольной сети без отрицательных циклов. Либо можно начать с истока в каждом сильно связном компоненте, либо добавить фиктивную вершину с ребром нулевой длины в каждую вершину сети. В любом случае получится остовный лес кратчайших путей, которым можно воспользоваться для присвоения весов вершинам (вес пути из корня в данную вершину в ее SPT). - -
- -## 4. Остовные деревья - -### 4.1. Остовное дерево. Построение с помощью обхода в глубину и в ширину - -#### Остовное дерево - -> **Остовное дерево** графа (англ. *Spanning tree*) — это дерево, подграф данного графа, с тем же числом вершин, что и у исходного графа. Неформально говоря, остовное дерево получается из исходного графа удалением максимального числа рёбер, входящих в циклы, но без нарушения связности графа. Остовное дерево включает в себя все n вершин исходного графа и содержит $n-1$ ребро. - -#### Построение с помощью обхода в глубину и в ширину - -Процедуры обхода графа в глубину и в ширину можно простым способом использовать для нахождения остовов. В обоих случаях достижение новой вершины графа $u$ из вершины $v$ вызывает "включение" в остовное дерево ребра $(u, v)$. - -```c++ -void Spisok::Depth_First_Search (Lref r) -//Рекурсивный обход графа в глубину. r - указатель -//на структуру Вирта. -{ - Tref t; - - t = (*r).Trail; cout<<(*r).Key; (*r).Flag = FALSE; - while (t!=NULL) - { if ((*(*t).Id).Flag) Depth_First_Search ((*t).Id); t = (*t).Next; } -} -``` - -```c++ -void Spisok::Ostov_Depth (Lref r) -//Рекурсивный обход графа в глубину, соединенный с -//нахождением ребра остовного дерева. -//r - указатель на структуру Вирта. -{ - Tref t; - Lref s; - - s = r; - t = (*r).Trail; (*r).Flag = FALSE; - while ( t != NULL ) - { if ((*(*t).Id).Flag) - { cout << "(" << s->Key << "," << t->Id->Key << ") "; - Ostov_Depth ((*t).Id); - } - t = (*t).Next; - } -} -``` - -### 4.2. Определение минимального остовного дерева - -> **Минимальное остовное дерево** (англ. *minimum spanning tree*) графа — это его ациклический связный подграф, в который входят все его вершины, обладающий минимальным суммарным весом ребер. Заметим, что граф может содержать несколько минимальных остовных деревьев. - -### 4.3. Теорема о разрезе. Доказательство - -#### Теорема о разрезе - -**Теорема Форда — Фалкерсо́на** — теорема о максимальном потоке в графе, тесно связанная с теоремой Менгера. -Звучит так: величина максимального потока в графе путей равна величине пропускной способности его минимального разреза. - -*Доказательство:* - -**Достаточность**: любой поток между вершинами t и s меньше или равен величине любого сечения. Пусть дан некоторый поток и некоторое сечение. Величина данного потока складывается из величин «грузов», перевозимых по всем возможным путям из вершины t в s. Каждый такой путь обязан иметь общее ребро с данным сечением. Так как по каждому ребру сечения суммарно нельзя перевести «груза» больше, чем его пропускная способность, поэтому сумма всех грузов меньше или равна сумме всех пропускных способностей рёбер данного сечения. Утверждение доказано. - -Отсюда следует, что любой поток меньше или равен величине минимального сечения, а значит и максимальный поток меньше или равен величине минимального сечения. - -### 4.4. Алгоритм Прима. Аналогия с алгоритмом Дейкстры. Оценка времени работы для различных реализаций очереди с приоритетом: бинарная куча, Фибоначчиева куча (последнее без доказательства) - -#### Алгоритм Прима - -> **Алгоритм Прима** (англ. *Prim's algorithm*) — алгоритм поиска минимального остовного дерева (англ. minimum spanning tree, MST) во взвешенном неориентированном связном графе. - -#### Аналогия с алгоритмом Дейкстры - -Данный алгоритм очень похож на алгоритм Дейкстры. Будем последовательно строить поддерево $F$ ответа графе $G$, поддерживая приоритетную очередь $Q$ из вершин $G \setminus F$, в которой ключом для вершины $v$ является $\min(\omega(uv) (u \in V(F), (u, v) \in E(G)))$ — вес минимального ребра из вершин $F$ в вершины $G \setminus F$. Также для каждой вершины в очереди будем хранить $p(v)$ — вершину $u$, на которой достигается минимум в определении ключа. Дерево $F$ поддерживается неявно, и его ребра — это пары $(v,p(v))$, где $v \in G \setminus {r} \setminus Q$, а $r$ — корень $F$. Изначально $F$ пусто и значения ключей у всех вершин равны $+\infty$. Выберём произвольную вершину $r$ и присвоим её ключу значение 0. На каждом шаге будем извлекать минимальную вершину $v$ из приоритетной очереди и релаксировать все ребра $(v, u)$, такие что $u \in Q$, выполняя при этом операцию $\rm decreaseKey$ над очередью и обновление $p(v)$. Ребро $(v,p(v))$ при этом добавляется к ответу. - -#### Оценка времени работы для различных реализаций очереди с приоритетом: бинарная куча, Фибоначчиева куча - -Двоичная куча - $O(ElogV)$ -(Чтобы упростить операцию $\rm decreaseKey$ можно написать кучу на основе сбалансированного бинарного дерева поиска. Тогда просто удалим вершину и добавим ее обратно уже с новым ключом. Асимптотика таких преобразований $O(logn)$. Если же делать с бинарной кучей, то вместо операции $\rm decreaseKey$, будем всегда просто добавлять вершину с новым ключом, если из кучи достали вершину с ключом, значение которого больше чем у нее уже стоит, просто игнорировать. Вершин в куче будет не больше $2 \cdot n$, следовательно, операция $\rm extractMin$ будет выполняться за $O(log(\cdot n))$, что равно $O(logn)$. Максимальное количество вершин, которое мы сможем достать, равняется количеству ребер, то есть m, поэтому общая асимптотика составит $O(mlogn)$, что хорошо только на разреженных графах.) - -Фибоначчиева куча - $O(VlogV+E)$ - -### 4.5. Алгоритм Прима. Доказательство корректности с помощью теоремы о разрезе - -Приведем еще раз теорему о разрезе: -> Для любого разбиения множества вершин $V = A \cup B$, существует минимальный остов, содержащий $e$ – минимальное по весу ребро, проходящее через разрез $\langle A, B \rangle$ - -Доказательство. Возьмём минимальный остов без $e$. Добавим $e$, получится цикл. Два ребра этого цикла проходят через разрез $\langle A, B \rangle$. Старое неменьше $e$, удалим его. Если же в процессе построения $\rm min$ остова уже известно подмножество рёбер будущего $\rm min$ остова, эти рёбра задают компоненты связности. Мы можем сжать компоненты в вершины и для конденсации применить лемму о разрезе. - -Следствие $X$ – подмножество рёбер некого минимального остова. Зафиксируем разрез $V = A \cup B$ такой, что все рёбра $X$ не пересекают разрез. Тогда $\exist$ минимальный остов, содержащий $X \cup \{e\}$, где $e$ – минимальное по весу ребро, проходящее через разрез $\langle A, B \rangle$. - -#### Доказательство корректности с помощью теоремы о разрезе - -Разрез – текущее множество $A$ и дополнение $V \setminus A$. - -### 4.6. Алгоритм Краскала. Доказательство корректности. Оценка времени работы - -#### Алгоритм Краскала - -Будем последовательно строить подграф $F$ графа $G$ ("растущий лес"), пытаясь на каждом шаге достроить $F$ до некоторого $MST$. Начнем с того, что включим в $F$ все вершины графа $G$. Теперь будем обходить множество $E(G)$ в порядке неубывания весов ребер. Если очередное ребро $e$ соединяет вершины одной компоненты связности $F$, то добавление его в остов приведет к возникновению цикла в этой компоненте связности. В таком случае, очевидно, $e$ не может быть включено в $F$. Иначе $e$ соединяет разные компоненты связности $F$, тогда существует $\langle S, T \rangle$ разрез такой, что одна из компонент связности составляет одну его часть, а оставшаяся часть графа — вторую. Тогда $e$ — минимальное ребро, пересекающее этот разрез. Значит, из леммы о безопасном ребре следует, что $e$ является безопасным, поэтому добавим это ребро в $F$. На последнем шаге ребро соединит две оставшиеся компоненты связности, полученный подграф будет минимальным остовным деревом графа $G$. Для проверки возможности добавления ребра используется система непересекающихся множеств. - -##### Доказательство корректности - -Обратимся к теореме о разрезе из Пункта 4.5 -Добавляем ребро $(a, b)$. Разрез – любой такой, что $(a, b)$ через него проходит. - -##### Оценка времени работы - -$O(ElogE)$. - -### 4.7. Система непересекающихся множеств. Эвристика потенциалов без доказательства. Эвристика сжатия пути без доказательства. Почти константное время работы - -### 4.8. Алгоритм Борувки. Доказательство(полное). Оценка времени работы - -#### Алгоритм Борувки - -Алгоритм состоит из нескольких шагов: - -1. Изначально каждая вершина графа $G$ — тривиальное дерево, а ребра не принадлежат никакому дереву. -2. Для каждого дерева $T$ найдем минимальное инцидентное ему ребро. Добавим все такие ребра. -3. Повторяем шаг 2 пока в графе не останется только одно дерево $T$. - -Данный алгоритм может работать неправильно, если в графе есть ребра равные по весу. Например, полный граф из трех вершин, вес каждого ребра равен один. В $T$ могут быть добавлены все три ребра. Избежать эту проблему можно, например, выбирая в первом пункте среди ребер, равных по весу, ребро с наименьшим номером. - -**Доказательство:** - -Обратимся к теореме о разрезе из Пункта 4.5 -Рёбра, которые хочет добавить Борувка, разобьём на компоненты связности. Каждая компонента – дерево, рёбра компоненты будем добавлять по одному от корня к листьям. Добавляя -очередное ребро ведущее в лист $v$, используем разрез $\langle \{v\}, V \setminus \{v\} \rangle$ и лемму о разрезе. - -##### Оценка времени работы - -На $i$-ой итерации внешнего цикла каждая компонента состоит как минимум из двух компонент из $(i−1)$-й итерации. Значит, на каждой итерации число компонент уменьшается как минимум в 2 раза. Тогда внешний цикл повторяется $O(logV)$ раз, так как количество компонент изначально равно количеству вершин. Что же касается внутреннего цикла, то он выполняется за $O(E)$, где $E$ — количество рёбер в исходном графе. Следовательно конечное время работы алгоритма $O(ElogV)$. - -### 4.9. Приближение решения задачи коммивояжера с помощью минимального остовного дерева - -> **Алгоритм Кристофидеса** или **алгоритм Кристофидеса-Сердюкова** — это алгоритм поиска приближённых решений задачи коммивояжёра для случаев, когда расстояния образуют метрическое пространство (симметричны и удовлетворяют неравенству треугольника). - -Алгоритм является аппроксимационным алгоритмом, который гарантирует, что решения находятся в пределах 3/2 от длины оптимального решения. Алгоритм назван именем Никоса Кристофидеса и Анатолия Ивановича Сердюкова, которые независимо друг от друга нашли его в 1976, и он обладает лучшим аппроксимационным коэффициентом, который был доказан для задачи коммивояжёра на метрических пространствах общего вида, хотя известны лучшие приближения для некоторых специальных случаев. - -Пусть $G=(V,w)$ будет представителем задачи коммивояжёра. То есть $G$ является полным графом на множестве вершин $V$, а функция $w$ назначает неотрицательные вещественные веса каждому ребру графа $G$. -Алгоритм можно описать на псевдокоде следующим образом: - -1. Создаём минимальное остовное дерев $T$ графа $G$. -2. Пусть $O$ будет набором вершин с нечётными степенями в $T$. Согласно лемме о рукопожатиях, $O$ имеет чётное число вершин. -3. Находим совершенное паросочетание $M$ минимального веса в порождённом подграфе, заданным вершинами из $O$. -4. Комбинируем рёбра $M$ и $T$ с образованием связного мультиграфа $H$, в котором каждая вершина имеет чётную степень. -5. Образуем эйлеров цикл в $H$. -6. Преобразуем цикл, найденный на предыдущем шаге, в гамильтонов цикл путём пропуска повторяющихся вершин (сокращение). - -
- -## 5. Потоки в сетях - -### 5.1. Определение сети. Определение потока. Физический смысл. Аналогия с законами Кирхгофа. Определение разреза. Понятия потока через разрез - -#### Определение сети - -> **Сеть** (англ. *flow network*) представляет собой ориентированный граф, в котором каждое ребро имеет положительную пропускную способность (англ. *capacity*). - -#### Определение потока - -> **Потоком** (англ. *flow*) $f$ в $G$ является действительная функция $f: V \times V \rightarrow R$, удоволетворяющая условиям: - -1) $f(u,v) = −f(v,u)$ (антисимметричность) -2) $f(u,v) \leq c(u,v)$ (ограничение пропускной способности), если ребра нет, то $f(u,v)=0$; -3) $\Sigma v \cdot f(u,v) = 0$ для всех вершин $u$, кроме $s$ и $t$ (закон сохранения потока). - -> Величина потока $f$ определяется как $|f|=\Sigma v \in V \cdot f(s,v)$. - -#### Физический смысл. Аналогия с законами Кирхгофа - -#### Определение разреза - -> $(s,t)$ - **разрезом** (англ. *s-t cut*) $\langle S,T \rangle$ в сети $G$ называется пара множеств $S$, $T$, удоволетворяющих условиям: $s \in S$,$t \in T$ и $S = V \setminus T$. - -#### Понятия потока через разрез - -> **Поток в разрезе** (англ. *flow in the cut*) $\langle S,T \rangle$ обозначается $f(S,T)$ и вычисляется по формуле: $f(S,T)= \Sigma u \in S \cdot \Sigma v \in T \cdot f(u,v)$. - -### 5.2. Доказательство факта, что поток через любой разрез одинаковый - -$f(S,T) = f(S,V) − f(S,S) = f(S,V) = f(S \setminus s,V) + f(s,V) = f(s,V) = |f|$ - -1-е равенство выполняется, так как суммы не пересекаются: $f(S,V)=f(S,S)+f(S,T)$ -2-е равенство выполняется из-за антисимметричности: $f(S,S)=−f(S,S)=0$ -3-е равенство выполняется, как и 1-е, из-за непересекающихся сумм -4-е равенство выполняется из-за сохранения потока - -### 5.3. Понятие остаточной сети. Понятие дополняющего пути. Необходимость отсутствия дополняющего пути для максимальности потока - -#### Понятие остаточной сети - -Если задана некоторая транспортная сеть $G$ и поток $f$, то **остаточная сеть** $G_f$ - это сеть, состоящая из ребер с пропускными способностями, указывающими, как могут меняться потоки через ребра $G$. - -#### Понятие дополняющего пути - -Для заданной транспортной сети и потока **дополняющим путем** (англ. *augmenting path*) является простой путь из истока в сток в остаточной сети. - -#### Необходимость отсутствия дополняющего пути для максимальности потока - -В случае существования дополняющего пути поток можно увеличить. - -### 5.4. Теорема Форда-Фалкерсона - -> **Теорема Форда — Фалкерсо́на** — теорема о максимальном потоке в графе, тесно связанная с теоремой Менгера. Звучит так: величина максимального потока в графе путей равна величине пропускной способности его минимального разреза. - -### 5.5. Алгоритм Форда-Фалкерсона. Поиск минимального разреза. Пример целочисленной сети, в котором алгоритм работает долго - -#### Алгоритм Форда-Фалкерсона - -Идея алгоритма заключается в следующем. Изначально величине потока присваивается значение 0: $f(u,v)=0$ для всех $u, v \in V$. Затем величина потока итеративно увеличивается посредством поиска увеличивающего пути (путь от источника $s$ к стоку $t$, вдоль которого можно послать ненулевой поток). В данной статье рассматривается алгоритм, осуществляющий этот поиск с помощью обхода в глубину (dfs). Процесс повторяется, пока можно найти увеличивающий путь. - -**Неформальное описание:** - -1. Обнуляем все потоки. Остаточная сеть изначально совпадает с исходной сетью. -2. В остаточной сети находим любой путь из источника в сток. Если такого пути нет, останавливаемся. -3. Пускаем через найденный путь (он называется увеличивающим путём или увеличивающей цепью) максимально возможный поток: - 1. На найденном пути в остаточной сети ищем ребро с минимальной пропускной способностью $c_{min}$. - 2. Для каждого ребра на найденном пути увеличиваем поток на $c_{min}$, а в противоположном ему — уменьшаем на $c_{min}$. - 3. Модифицируем остаточную сеть. Для всех рёбер на найденном пути, а также для противоположных (антипараллельных) им рёбер, вычисляем новую пропускную способность. Если она стала ненулевой, добавляем ребро к остаточной сети, а если обнулилась, стираем его. -4. Возвращаемся на шаг 2. - -#### Поиск минимального разреза - -Алгоритм Форда-Фалкерсона использует поиск в ширину для нахождения увеличивающего пути в сети и постепенно увеличивает поток через этот путь, пока не будет достигнут максимальный поток. Затем алгоритм ищет минимальный разрез, основываясь на значении потока. - -### 5.6. Алгоритм Эдмондса-Карпа. Доказательство, что кратчайшее расстояние в остаточной сети не уменьшается - -#### Алгоритм Эдмондса-Карпа - -> **Алгоритм Эдмондса-Карпа** является реализацией метода Форда-Фалкерсона, в которой в качестве дополняющего пути выбирается кратчайший по рёбрам путь в остаточной сети (длины всех рёбер равны 1). - -**Описание:** - -1. Положим все потоки равными нулю. Остаточная сеть изначально совпадает с исходной сетью. -2. В остаточной сети находим кратчайший путь из источника в сток. Если такого пути нет, останавливаемся. -3. Пускаем через найденный путь (он называется увеличивающим путём или увеличивающей цепью) максимально возможный поток: - 1. На найденном пути в остаточной сети ищем ребро с минимальной пропускной способностью $c_{min}$. - 2. Для каждого ребра на найденном пути увеличиваем поток на $c_{min}$, а в противоположном ему — уменьшаем на $c_{min}$. - 3. Модифицируем остаточную сеть. Для всех рёбер на найденном пути, а также для противоположных им рёбер, вычисляем новую пропускную способность. Если она стала ненулевой, добавляем ребро к остаточной сети, а если обнулилась, стираем его. -4. Возвращаемся на шаг 2. - -**Доказательство, что кратчайшее расстояние в остаточной сети не уменьшаетcя:** - -**Лемма:** - -> Кратчайшее расстояние от истока до каждой вершины не уменьшается с выполнением каждой итерации, т.е. $level_{i+1}[v] >= level_i[v]$, где нижний индекс обозначает номер фазы, перед которой взяты значения этих переменных. - -**Доказательство:** - -Зафиксируем произвольную фазу $i$ и произвольную вершину $v$ и рассмотрим любой кратчайший $s - v$ путь $P$ в сети $G^R_{i+1}$ (напомним, так мы обозначаем остаточную сеть, взятую перед выполнением $i+1$-ой фазы). Очевидно, длина пути $P$ равна $level_{i+1}[v]$. - -Заметим, что в остаточную сеть $G^R_{i+1}$ могут входить только рёбра из $G^R$, а также рёбра, обратные рёбрам из $G^R$ (это следует из определения остаточной сети). Рассмотрим два случая: - -Путь $P$ содержит только рёбра из $G^R$. Тогда, понятно, длина пути $P$ больше либо равна $level_i[v]$ (потому что $level_i[v]$ по определению — длина кратчайшего пути), что и означает выполнение неравенства. Путь $P$ содержит как минимум одно ребро, не содержащееся в $G^R$ (но обратное какому-то ребру из $G^R$). Рассмотрим первое такое ребро; пусть это будет ребро $(u,w)$. $s \rightarrow u \rightarrow w$ - -Мы можем применить нашу лемму к вершине $u$, потому что она подпадает под первый случай; итак, мы получаем неравенство $level_{i+1}[u] >= level_i[u]$. - -Теперь заметим, что поскольку ребро $(u, w)$ появилось в остаточной сети только после выполнения $i$-ой фазы, то отсюда следует, что вдоль ребра $(w, u)$ был дополнительно пропущен какой-то поток; следовательно, ребро $(w, u)$ принадлежало слоистой сети перед $i$-ой фазой, а потому $level_i[u] = level_i[w] + 1$. Учтём, что по свойству кратчайших путей $level_{i+1}[w] = level_{i+1}[u] + 1$, и объединяя это равенство с двумя предыдущими неравенствами, получаем: -$level_{i+1}[w] >= level_i[w] + 2$. - -Теперь мы можем применять те же самые рассуждения ко всему оставшемуся пути до $v$ (т.е. что каждое инвертированное ребро добавляет к $\rm level$ как минимум два), и в итоге получим требуемое неравенство. - -### 5.7. Общая оценка времени работы алгоритма Эдмондса-Карпа - -Сложность алгоритма Эдмондса-Карпа равна $O(VE^2)$. - -### 5.8. Слоистая сеть. Алгоритм Диница. Оценка времени работы без доказательства - -#### Слоистая сеть - -> **Слоистая сеть** — это сеть состоящая только из таких рёбер $(v, u)$ сети $G$, что $d(v) + 1 = d(u)$, где $d$ - расстояние от истока. - -#### Алгоритм Диница - -**Схема алгоритма:** - -1. Построить слоистую сеть по остаточной, если сток не достижим то завершить алгоритм -2. Пустить блокирующий поток в слоистой сети -3. Если алгоритм завершился, то в остаточной сети сток не достижим из истока, а значит не существует увеличивающего пути, следовательно найденный поток максимален. - -#### Оценка времени работы без доказательства - -Так как расстояние строго увеличивается количество итераций алгоритма = $O(V)$. В зависимости от выбора алгоритма поиска блокирующего потока время данного алгоритма может составить $O(VE^2), O(V^2E)$ или $O(V^3)$. - -
- -## 6. RMQ. Sparse-table, дерево отрезков. LCA. Декартово дерево по неявному ключу - -### 6.1. RSQ и RMQ. Sparse-table - -#### RSQ и RMQ. -Дан числовой массив: $A[0]$, $A[1]$, ..., $A[N−1$.$ - -Нужно отвечать на запросы: - -- для данных $L$ и $R$ вычислить сумму $A[L] + ... + A[R−1]$; -- (опционально) обновить элемент: $A[i] := x$. - -Операция — не обязательно сумма, а элементы — не обязательно числа. - -- Сумма $\rightarrow$ RSQ (range sum query) -- Минимум $\rightarrow$ RMQ (range minimum query) - -### 6.2. Дерево отрезков. Обработка запросов от листьев. Обработка запросов от корня - -#### Дерево отрезков - -Дерево отрезков — это структура данных, которая позволяет эффективно (т.е. за асимптотику $O(log n)$) реализовать операции следующего вида: нахождение суммы/минимума элементов массива в заданном отрезке ($a[l ... r]$, где $l$ и $r$ поступают на вход алгоритма), при этом дополнительно возможно изменение элементов массива: как изменение значения одного элемента, так и изменение элементов на целом подотрезке массива (т.е. разрешается присвоить всем элементам $a[l ... r]$ какое-либо значение, либо прибавить ко всем элементам массива какое-либо число). - -Вообще, дерево отрезков — очень гибкая структура, и число задач, решаемых ей, теоретически неограниченно. Помимо приведённых выше видов операций с деревьями отрезков, также возможны и гораздо более сложные операции (см. раздел "Усложнённые версии дерева отрезков"). В частности, дерево отрезков легко обобщается на большие размерности: например, для решения задачи о поиске суммы/минимума в некотором подпрямоугольнике данной матрицы (правда, уже только за время $O(log^2 n)$). - -Важной особенностью деревьев отрезков является то, что они потребляют линейный объём памяти: стандартному дереву отрезков требуется порядка $4n$ элементов памяти для работы над массивом размера $n$. - -#### Обработка запросов от листьев. Обработка запросов от корня - -В дереве отрезков обработка запросов может осуществляться как от листьев (bottom-up), так и от корня (top-down). Это связано с тем, как информация распространяется вдоль дерева при выполнении запросов. - -1. **Обработка запросов от листьев (Bottom-Up):** - - Идея заключается в том, что сначала вычисляются значения для листьев (первоначальные элементы массива), а затем эта информация постепенно передается вверх по дереву для расчета значений на более высоких уровнях. - - Когда поступает запрос на определенный отрезок, начинаются вычисления от соответствующих листьев. Затем результаты объединяются на более высоких уровнях дерева, пока не достигнется корень. - - Этот подход обеспечивает эффективность расчетов для запросов на отрезках. - -2. **Обработка запросов от корня (Top-Down):** - - Здесь запрос начинается с корня дерева, и информация передается вниз по дереву до тех пор, пока не достигнут нужные листья (или пока не выполнены условия запроса). - - Этот метод может быть полезен в случаях, когда запросы связаны с каким-то условием, которое позволяет пропускать некоторые поддеревья, не вычисляя их полностью. - - Позволяет оптимизировать запросы, если известно, что результаты на некоторых уровнях дерева не повлияют на результат запроса. - -Реализация обработки запросов от листьев или от корня зависит от конкретных требований задачи и структуры дерева отрезков. В некоторых случаях может быть полезно использовать комбинацию обоих методов для оптимизации производительности и удовлетворения специфических требований запросов. - -### 6.3. Изменение значения в массиве, обновление дерева отрезков. Множественные операции - -#### Изменение значения в массиве, обновление дерева отрезков. - -Напомним, что запрос обновления получает на вход индекс $i$ и значение $x$, и перестраивает дерево отрезков таким образом, чтобы оно соответствовало новому значению $A[i] := x$. Этот запрос должен также выполняться за время $O(log n)$. - -Это более простой запрос по сравнению с запросом подсчёта суммы. Дело в том, что элемент $A[i]$ участвует только в относительно небольшом числе вершин дерева отрезков: а именно, в $O(log n)$ вершинах — по одной с каждого уровня. - -Тогда понятно, что запрос обновления можно реализовать как рекурсивную функцию: ей передаётся текущая вершина дерева отрезков, и эта функция выполняет рекурсивный вызов от одного из двух своих сыновей (от того, который содержит позицию i в своём отрезке), а после этого — пересчитывает значение суммы в текущей вершине точно таким же образом, как мы это делали при построении дерева отрезков (т.е. как сумма значений по обоим сыновьям текущей вершины). - -```c++ -void update (int v, int tl, int tr, int pos, int new_val) { - if (tl == tr) - t[v] = new_val; - else { - int tm = (tl + tr) / 2; - if (pos <= tm) - update (v*2, tl, tm, pos, new_val); - else - update (v*2+1, tm+1, tr, pos, new_val); - t[v] = t[v*2] + t[v*2+1]; - } -} -``` - -#### Множественные операции - -Множественные операции в дереве отрезков относятся к расширенной концепции структуры данных "дерево отрезков", которая позволяет эффективно выполнять несколько операций на отрезках массива или последовательности данных. Обычно дерево отрезков предназначено для выполнения операций типа запроса на сумму, минимум, максимум на определенном подотрезке массива. Однако, при необходимости, можно расширить возможности дерева отрезков для поддержки нескольких операций. - -Примеры множественных операций в дереве отрезков могут включать в себя: - -1. **Сумма и обновление (Range Sum Query and Range Update):** Помимо запросов на сумму элементов на определенном отрезке, можно добавить операции обновления, изменяющие значения элементов на определенном отрезке. - -2. **Минимум/максимум и обновление (Range Minimum/Maximum Query and Range Update):** Поддержка запросов на поиск минимума или максимума на определенном отрезке, а также операций обновления значений элементов. - -3. **Подсчет количества элементов в диапазоне с заданным условием (Count Elements in Range with a Given Condition):** Дерево отрезков может быть настроено для подсчета количества элементов, удовлетворяющих определенному условию, на определенном отрезке. - -4. **Операции нахождения k-го порядкового статистического элемента (K-th Order Statistics Operations):** Поиск $k$-го минимального/максимального элемента на определенном отрезке. - -5. **Сложные агрегатные функции:** Различные агрегатные функции, такие как нахождение среднего значения, медианы и др., могут быть реализованы с использованием множественных операций. - -Реализация множественных операций требует изменений в структуре дерева отрезков и логике обработки запросов и обновлений. Это может потребовать более сложного кода, но в то же время предоставляет более широкий спектр возможностей для решения различных задач. - -### 6.4. LCA. Метод двоичного подъёма - -#### LCA - -Наименьший общий предок (нижайший общий предок) вершин $u$ и $v$ в корневом дереве $T$ — наиболее удалённая от корня дерева вершина, лежащая на обоих путях от $u$ и $v$ до корня, т. е. являющаяся предком обеих вершин. Общепринятое сокращение — $LCA$ от англ. *lowest (least) common ancestor*. - -#### Метод двоичного подъёма - -> **Метод двоичного подъёма** — один из самых простых методов для решения задачи LCA в online. Он не использует метод решения задачи RMQ и основан на методе динамического программирования. - -```c++ -function preprocess(): - int[] p = dfs(0) - for i = 1 to n - dp[i][0] = p[i] - for j = 1 to log(n) - for i = 1 to n - dp[i][j] = dp[dp[i][j - 1]][j - 1] - - int lca(int v, int u): - if d[v] > d[u] - swap(v, u) - for i = log(n) downto 0 - if d[dp[u][i]] - d[v] >= 0 - u = dp[u][i] - if v == u - return v - for i = log(n) downto 0 - if dp[v][i] != dp[u][i] - v = dp[v][i] - u = dp[u][i] - return p[v] -``` - -### 6.5. Декартово дерево по неявному ключу. Интерфейс быстрого массива: Доступ к элементу в позиции i, Вставка элемента в позицию i, Удаление элемента из позиции i, Конкатенация двух массивов, разделение массива на два - -#### Декартово дерево по неявному ключу - -Декартово дерево по неявному ключу - это структура данных, объединяющая свойства декартова дерева и быстрого массива (cartesian tree on implicit key). Декартово дерево основано на двух параметрах: приоритете (обычно случайном) и ключе элемента. В случае декартова дерева по неявному ключу, элементы упорядочены по порядку, заданному индексами массива. - -#### Интерфейс быстрого массива - -1. **Доступ к элементу в позиции i:** - - Так как элементы упорядочены по неявному ключу (порядковому номеру), доступ к элементу в позиции i выполняется, обращаясь к элементу с ключом i. - -2. **Вставка элемента в позицию i:** - - Вставка элемента в позицию i может быть выполнена путем создания нового узла с соответствующим приоритетом и неявным ключом i, затем соединения этого нового узла с существующими поддеревьями вокруг позиции i. - -3. **Удаление элемента из позиции i:** - - Удаление элемента из позиции i подразумевает разделение декартова дерева на два поддерева: одно до позиции i и второе после позиции i. Затем оба поддерева могут быть объединены, игнорируя узел с ключом i. - -Декартово дерево по неявному ключу обеспечивает эффективный доступ к элементам по индексу, а также эффективную вставку и удаление элементов, поддерживая при этом свойства декартова дерева. - -#### Конкатенация двух массивов - -Декартово дерево можно использовать для эффективной конкатенации двух массивов и разделения массива на две части, представляя массив в виде декартова дерева по неявному ключу. - -Для конкатенации двух массивов, представленных в виде декартова дерева, можно выполнить следующие шаги: - -1. Создать новый узел, который станет корнем нового дерева. -2. Как корень нового дерева выбрать узел с максимальным приоритетом из обоих деревьев. -3. Сделать корень дерева с максимальным приоритетом корнем нового дерева. -4. Поддерево с меньшим приоритетом становится левым (или правым) поддеревом корня нового дерева. -5. Рекурсивно повторить процесс для поддеревьев, соответствующих двум исходным деревьям. - -#### Разделение массива на два - -Для разделения массива на две части в декартовом дереве, можно воспользоваться тем фактом, что приоритеты узлов определяют порядок элементов. Мы ищем узел, чей неявный ключ соответствует индексу разделения. - -1. Выполняем поиск в декартовом дереве по индексу разделения. -2. Получаем поддерево с корнем, у которого неявный ключ равен индексу разделения. -3. Разделяем это поддерево на два: левое поддерево становится левой частью, а правое поддерево становится правой частью. diff --git a/doc/theory.md b/doc/theory.md index 8a6bd92..72ebf4d 100644 --- a/doc/theory.md +++ b/doc/theory.md @@ -53,8 +53,8 @@ - [Алгоритм A\*](#алгоритм-a) - [Примеры эвристик](#примеры-эвристик) - [3.4. Алгоритм Форда-Беллмана. Хранение в матрице: $D\_{vk}$ равно длине кратчайшего пути до вершины $v$ за ровно $k$ ребер (не более $k$ ребер). Доказательство корректности (полное). Оценка времени работы](#34-алгоритм-форда-беллмана-хранение-в-матрице-d_vk-равно-длине-кратчайшего-пути-до-вершины-v-за-ровно-k-ребер-не-более-k-ребер-доказательство-корректности-полное-оценка-времени-работы) - - [Доказательство корректности(полное)](#доказательство-корректностиполное) - - [Оценка времени работы](#оценка-времени-работы-1) + - [Доказательство корректности алгоритма Форда-Беллмана](#доказательство-корректности-алгоритма-форда-беллмана) + - [Оценка времени работы алгоритма Форда-Беллмана](#оценка-времени-работы-алгоритма-форда-беллмана) - [3.5. Восстановление пути. Детектирование цикла отрицательного веса. Поиск самого цикла](#35-восстановление-пути-детектирование-цикла-отрицательного-веса-поиск-самого-цикла) - [Восстановление пути](#восстановление-пути) - [Детектирование цикла отрицательного веса](#детектирование-цикла-отрицательного-веса) @@ -63,7 +63,7 @@ - [3.7. Алгоритм Флойда. Доказательство (концепция). Восстановление пути](#37-алгоритм-флойда-доказательство-концепция-восстановление-пути) - [Алгоритм Флойда](#алгоритм-флойда) - [Доказательство (концепция)](#доказательство-концепция) - - [Восстановление пути](#восстановление-пути-1) + - [Восстановление пути в алгоритме Флойда](#восстановление-пути-в-алгоритме-флойда) - [3.8. Нахождение цикла отрицательного веса](#38-нахождение-цикла-отрицательного-веса) - [3.9. Алгоритм Джонсона. Добавление фиктивного корня и фиктивных ребер для запуска алгоритма Форда-Беллмана](#39-алгоритм-джонсона-добавление-фиктивного-корня-и-фиктивных-ребер-для-запуска-алгоритма-форда-беллмана) - [Алгоритм Джонсона](#алгоритм-джонсона) @@ -86,7 +86,7 @@ - [4.7. Система непересекающихся множеств. Эвристика потенциалов без доказательства. Эвристика сжатия пути без доказательства. Почти константное время работы](#47-система-непересекающихся-множеств-эвристика-потенциалов-без-доказательства-эвристика-сжатия-пути-без-доказательства-почти-константное-время-работы) - [4.8. Алгоритм Борувки. Доказательство. Оценка времени работы](#48-алгоритм-борувки-доказательство-оценка-времени-работы) - [Алгоритм Борувки](#алгоритм-борувки) - - [Оценка времени работы](#оценка-времени-работы-2) + - [Оценка времени работы алгоритма Борувки](#оценка-времени-работы-алгоритма-борувки) - [4.9. Приближение решения задачи коммивояжера с помощью минимального остовного дерева](#49-приближение-решения-задачи-коммивояжера-с-помощью-минимального-остовного-дерева) - [5. Потоки в сетях](#5-потоки-в-сетях) - [5.1. Определение сети. Определение потока. Физический смысл. Аналогия с законами Кирхгофа. Определение разреза. Понятия потока через разрез](#51-определение-сети-определение-потока-физический-смысл-аналогия-с-законами-кирхгофа-определение-разреза-понятия-потока-через-разрез) @@ -118,7 +118,7 @@ - [Дерево отрезков](#дерево-отрезков) - [Обработка запросов от листьев. Обработка запросов от корня](#обработка-запросов-от-листьев-обработка-запросов-от-корня) - [6.3. Изменение значения в массиве, обновление дерева отрезков. Множественные операции](#63-изменение-значения-в-массиве-обновление-дерева-отрезков-множественные-операции) - - [Изменение значения в массиве, обновление дерева отрезков.](#изменение-значения-в-массиве-обновление-дерева-отрезков) + - [Изменение значения в массиве, обновление дерева отрезков](#изменение-значения-в-массиве-обновление-дерева-отрезков) - [Множественные операции](#множественные-операции) - [6.4. LCA. Метод двоичного подъёма](#64-lca-метод-двоичного-подъёма) - [LCA](#lca) @@ -129,7 +129,6 @@ - [Конкатенация двух массивов](#конкатенация-двух-массивов) - [Разделение массива на два](#разделение-массива-на-два) - ## 1. Обходы графа ### 1.1. Ориентированный граф, псевдограф. Неориентированный граф, псевдограф. Связность в неориентированном графе, компоненты связности. Слабая и сильная связность в ориентированном графе. Компоненты слабой, сильной связности @@ -188,7 +187,6 @@ void dfs (int vertex) { } ``` - #### Лемма о белых путях **Лемма**: Не существует такого момента выполнения поиска в глубину, в который бы существовало ребро из черной вершины в белую. @@ -215,7 +213,6 @@ int dfs(int vertex, std::vector visited): return visitedVertices ``` - #### Поиск цикла в неориентированном и ориентированном графе *Условие:* @@ -768,7 +765,7 @@ bool fordBellman(s): return true ``` -#### Доказательство корректности(полное) +#### Доказательство корректности алгоритма Форда-Беллмана Во-первых, сразу заметим, что для недостижимых из $v$ вершин алгоритм отработает корректно: для них метка $d[]$ так и останется равной бесконечности (т.к. алгоритм Форда-Беллмана найдёт какие-то пути до всех достижимых из $s$ вершин, а релаксация во всех остальных вершинах не произойдёт ни разу). @@ -782,7 +779,7 @@ bool fordBellman(s): Последнее, что надо заметить — это то, что любой кратчайший путь не может иметь более $n-1$ ребра. Следовательно, алгоритму достаточно произвести только $n-1$ фазу. После этого ни одна релаксация гарантированно не может завершиться улучшением расстояния до какой-то вершины. -#### Оценка времени работы +#### Оценка времени работы алгоритма Форда-Беллмана Инициализация занимает $\theta(V)$ времени, каждый из $|V|−1$ проходов требует $\theta(E)$ времени, обход по всем ребрам для проверки наличия отрицательного цикла занимает $O(E)$ времени. Значит алгоритм Беллмана-Форда работает за $O(VE)$ времени. @@ -879,7 +876,7 @@ new_d[i][j] = min(d[i][j], d[i][k] + d[k][j]); Доказательство "правильности" работы этого алгоритма также очевидно и выполняется с помощью математической индукции по $k$, показывая, что на $k$-й итерации вершина $k$ включается в путь только тогда, когда новый путь короче старого. -#### Восстановление пути +#### Восстановление пути в алгоритме Флойда Легко поддерживать дополнительную информацию — так называемых "предков", по которым можно будет восстанавливать сам кратчайший путь между любыми двумя заданными вершинами в виде последовательности вершин. @@ -1042,7 +1039,7 @@ void Spisok::Ostov_Depth (Lref r) Рёбра, которые хочет добавить Борувка, разобьём на компоненты связности. Каждая компонента – дерево, рёбра компоненты будем добавлять по одному от корня к листьям. Добавляя очередное ребро ведущее в лист $v$, используем разрез $\langle \{v\}, V \setminus \{v\} \rangle$ и лемму о разрезе. -##### Оценка времени работы +##### Оценка времени работы алгоритма Борувки На $i$-ой итерации внешнего цикла каждая компонента состоит как минимум из двух компонент из $(i−1)$-й итерации. Значит, на каждой итерации число компонент уменьшается как минимум в 2 раза. Тогда внешний цикл повторяется $O(logV)$ раз, так как количество компонент изначально равно количеству вершин. Что же касается внутреннего цикла, то он выполняется за $O(E)$, где $E$ — количество рёбер в исходном графе. Следовательно конечное время работы алгоритма $O(ElogV)$. @@ -1241,7 +1238,7 @@ $\rm level_{i+1}[w] \geq level_i[w] + 2$. ### 6.3. Изменение значения в массиве, обновление дерева отрезков. Множественные операции -#### Изменение значения в массиве, обновление дерева отрезков. +#### Изменение значения в массиве, обновление дерева отрезков Напомним, что запрос обновления получает на вход индекс $i$ и значение $x$, и перестраивает дерево отрезков таким образом, чтобы оно соответствовало новому значению $A[i] := x$. Этот запрос должен также выполняться за время $O(log n)$.