diff --git a/doc/Paragraph_1.md b/doc/Paragraph_1.md new file mode 100644 index 0000000..715cc5e --- /dev/null +++ b/doc/Paragraph_1.md @@ -0,0 +1,110 @@ + + +### 1.1. Ориентированный граф, псевдограф. Неориентированный граф, псевдограф. Связность в неор. графе, компоненты связности. Слабая и сильная связность в ор. графе. Компоненты слабой, сильной связности. + $\textbf{1.1.1 Граф}$ - упорядоченная пара $G$($V$, $E$), где $V$ - множество вершин, а $E$ $\subset$ ($V$ x $V$) - ребра. \ + \ + $\textbf{1.1.2 Ориентированный граф}$ - граф, в котором у каждого ребра есть направление, то есть стартовая и конечная вершины. \ + \ + $\textbf{1.1.3 Мультиграф}$ - граф, в котором хотя бы одна пара вершин соединена более чем одним ребром. \ + \ + $\textbf{1.1.4 Псевдограф}$ Псевдограф - мультиграф, в котором есть петли. \ + \ + $\textbf{1.1.5}$ Две вершины $u$ и $v$ **достижимы**, если найдется такая цепь из вершин $v$, $v_1$, $v_2$, ..., $v_n$, $u$, что $v_i$ смежна с $v_{i+1}$, а также смежны $v$, $v_1$ и $v_n$, $u$. \ + \ + $\textbf{1.1.6}$ Неориентированный граф **связен**, если для любой вершины все остальные достижимы из нее.\ + \ + $\textbf{1.1.7 Компонента связности в графе}$ - множество вершин графа достижимых попарно и рёбра их связывающие.\ + \ + $\textbf{1.1.8 Слабая связность в ориентированном графе}$ - ориентированный граф не связен, но его неориентированная копия связна.\ + \ + $\textbf{1.1.9 Сильная связность в оринетированном графе}$ - достижимость из произвольной вершины графа в любую другую.\ + \ + $\textbf{1.1.10 Компонента сильной связности в графе G}$ - подграф $G'$, который сильно связан.\ + \ + $\textbf{1.1.11 Компонента слабой связности в графе G}$ - подграф $G'$, который слабо связан. \ +### 1.2. Обход в глубину. Цвета вершин. Времена входа и выхода. Лемма о белых путях(с доказательством). + \ + $\textbf{1.2.1 Обход в глубину (DFS)}$ - это рекурсивный алгоритм по поиску всех вершин графа или дерева. Обход подразумевает под собой посещение всех вершин графа.\ + \ + $\quad$ **Алгоритм:** \ + $\quad$ $\quad$ $1.$ Выбираем любую вершину $v$ из еще не посещенных. \ + $\quad$ $\quad$ $2.$ Помечаем ее как пройденную. \ + $\quad$ $\quad$ $3.$ Повторяем первые два шага к смежным к $v$ вершинам. \ + \ + $\textbf{1.2.2 Цвета вершин в DFS}$ \ + $\quad$ **Белый** - вершина еще не была посещена. \ + $\quad$ **Серый** - вершина в процессе обхода. \ + $\quad$ **Черный** - вершина, у которой посещены все смежные ей вершины. \ + \ + $\textbf{1.2.3 Времена входа и выхода в DFS}$ - назовем временами входа и выхода пару чисел $entry[u], leave[u]$. Массивы $leave$ и $entry$ будем заполнять в ходе модифицированного DFS. \ + \ + $\textbf{1.2.4 Модифицированный DFS.}$ + \ + $\quad$ **Алгоритм:** \ + $\quad$ $\quad$ $1.$ Все вершины в начале алгоритма белые. \ + $\quad$ $\quad$ $2.$ Заводим переменную $time = $ 0. \ + $\quad$ $\quad$ $3.$ Выберем произвольную вершину $u$, $entry[u] = time$. \ + $\quad$ $\quad$ $4.$ Красим вершину $u$ в серый цвет, инкрементируем $time$. \ + $\quad$ $\quad$ $5.$ Для каждой белой вершины, смежной с $u$, запускаем DFS. \ + $\quad$ $\quad$ $6.$ Красим $u$ в черный цвет, инкременитруем $time$, $leave[u] = time$. \ + \ + $\textbf{1.2.5 Лемма о белых путях.}$ \ + $\quad$ Пусть дан граф $G$. Запустим DFS($G$). Остановим выполнение процедуры DFS от какой-то вершины $u$ в тот момент, когда вершина $u$ была выкрашена в серый цвет (назовем первым моментом времени). Заметим, что в данный момент в графе $G$ есть как белые, так и черные и серые вершины. Продолжим выполнение процедуры DFS($u$) до того момента, когда вершина $u$ станет черной (второй момент времени). Тогда вершины графа $G$ \ {$u$}, бывшие черными и серыми в первый момент времени, не поменяют свой цвет ко второму моменту времени, а белые вершины либо останутся белыми, либо станут черными, причем черными станут те, что были достижимы от вершины $u$ по белым путям. \ + + **Доказательство:** \ + $\quad$ Черные вершины останутся черными, потому что цвет может меняться только + по схеме белый, серый, черный. Серые останутся серыми, потому что они лежат в стеке рекурсии и + там и останутся. \ + \ + $\quad$ Заметим, что не существует такого момента в процессе обхода, что существует ребро из черной $v$ + вершины в белую $u$. Действительно, запустим DFS($v$). В этот момент $v$ стала серой, а 𝑢 была белой. + Далее будет запущен DFS($u$), так как $u$ была белой. По алгоритму вершина $v$ будет покрашена в + черный цвет тогда, когда завершится обход всех вершин, достижимых из нее по одному ребру, кроме + тех, что были рассмотрены раньше нее. Таким образом, вершина $v$ может стать черной только тогда, + когда DFS выйдет из вершины $u$, и она будет покрашена в черный цвет. Получаем противоречие. \ + \ + $\quad$ Теперь заметим, что если вершина достижима по пути из белых вершин в первый момент времени, + то она стала черной ко сторому моменту времени (из абзаца выше следует). + Заметим, что это верно и в обратную сторону. Рассмотрим момент, когда вершина $v$ стала черной: + в этот момент существует cерый путь из $u$ в $v$, а это значит, что в первый момент времени сущестовал + белый путь из $u$ в $v$. \ + \ + $\quad$ Отсюда следует, что если вершина была перекрашена из белой в черную, то она была достижима + по белому пути, и что если вершина как была, так и осталась белой, она не была достижима по + белому пути, что и требовалось доказать. \ + ### 1.3. Проверка связности неориентированного графа. Поиск цикла в неориентированном и ориентированном графе. Топологическая сортировка. + $\textbf{1.3.1 Проверка связности ориентированного графа.}$ - модифицируем DFS так, чтобы он возвращал число посещенных вершин. Тогда запустим его от произвольной вершины, и если возвращенное число равно числу вершин в графе, то граф связный. \ + \ + $\textbf{1.3.2 Поиск цикла в неориентированном и ориентированном графе.}$ \ + $\quad$ Будем решать задачу с помощью поиска в глубину. \ + \ + $\quad$ В случае ориентированного графа произведём серию обходов. То есть из каждой вершины, в + которую мы ещё ни разу не приходили, запустим поиск в глубину, который при входе в вершину + будет красить её в серый цвет, а при выходе из нее — в чёрный. И, если алгоритм пытается пойти в + серую вершину, то это означает, что цикл найден. \ + \ + $\quad$ В случае неориентированного графа, одно ребро не должно встречаться в цикле дважды по определению. Поэтому необходимо дополнительно проверять, что текущее рассматриваемое из вершины + ребро не является тем ребром, по которому мы пришли в эту вершину. \ + \ + $\textbf{1.3.3 Топологическая сортировка.}$ \ + $\quad$ Топологическая сортировка ориентированного ациклического графа $G(V, E)$ представляет собой + упорядочивание вершин таким образом, что для любого ребра ($u$, $v$) $\in$ $E$ номер вершины 𝑢 меньше + номера вершины $u$. \ + \ + $\quad$ Предположим, что граф ацикличен, т.е. решение существует. Что делает обход в глубину? При + запуске из какой-то вершины $v$ он пытается запуститься вдоль всех рёбер, исходящих из $v$. Вдоль + тех рёбер, концы которых уже были посещены ранее, он не проходит, а вдоль всех остальных — + проходит и вызывает себя от их концов. \ + \ + $\quad$ Таким образом, к моменту выхода из вызова DFS($v$) все вершины, достижимые из $v$ как непосредственно (по одному ребру), так и косвенно (по пути)— все такие вершины уже посещены обходом. Следовательно, если мы будем в момент выхода из DFS($v$) добавлять нашу вершину в начало некоего списка, то в конце концов в этом списке получится топологическая сортировка. \ + ### 1.4. Нахождение компонент сильной связности. Алгоритм Косарайю. Алгоритм Тарьяна. + \ + $\textbf{1.4.1 нахождение компонент сильной связности. Алгоритм Косарайю}$ \ + $\quad$ **Алгоритм :** \ + $\quad$ $\quad$ $1.$ Строим граф $H$ на основе данного графа $G$, инвертируя все ребра. \ + $\quad$ $\quad$ $2.$ Запускаем DFS на этом графе, вычисляющий для каждой вершины время выхода DFS из нее. Пусть эти данные будут находиться в массиве $outTime$. \ + $\quad$ $\quad$ $3.$ Выполняем DFS на исходном графе, перебирая вершины в порядке убывания $outTime[u]$. \ + \ + Подробнее про работу алгоритма рассказывается в этом видео: https://www.youtube.com/watch?v=-UgiBh1IMQU&t=704s \ + \ + diff --git a/doc/image.png b/doc/image.png new file mode 100644 index 0000000..c04afbe Binary files /dev/null and b/doc/image.png differ diff --git a/task_01/src/main.cpp b/task_01/src/main.cpp index 0e4393b..580abd3 100644 --- a/task_01/src/main.cpp +++ b/task_01/src/main.cpp @@ -1,3 +1,21 @@ +#include +#include #include +#include -int main() { return 0; } +#include "topology_sort.hpp" + +int main() { + int n, m; + std::cin >> n >> m; + + std::vector > graph; + graph.resize(n); + int tmp_from, tmp_to; + for (int i = 0; i < m; ++i) { + std::cin >> tmp_from >> tmp_to; + graph[tmp_from].push_back(tmp_to); + } + + std::vector vec = top_sort(graph); +} \ No newline at end of file diff --git a/task_01/src/test.cpp b/task_01/src/test.cpp index ef5a86a..8a3c282 100644 --- a/task_01/src/test.cpp +++ b/task_01/src/test.cpp @@ -4,5 +4,39 @@ #include "topology_sort.hpp" TEST(TopologySort, Simple) { - ASSERT_EQ(1, 1); // Stack [] + std::vector> graph = {{2, 5, 4}, {3, 5, 6}, {}, {2}, + {}, {2, 3, 4}, {3}, {5}}; + std::vector ans = {7, 1, 6, 0, 5, 4, 3, 2}; + ASSERT_EQ(top_sort(graph), ans); } + +TEST(TopologySort, Cycle) { + std::vector> graph = {{1}, {2}, {0, 1, 2}, {}}; + std::vector ans = {3, 0, 1, 2}; + ASSERT_EQ(top_sort(graph), ans); +} + +TEST(TopologySort, Empty_graph) { + std::vector> graph = {{0}}; + std::vector ans = {0}; + ASSERT_EQ(top_sort(graph), ans); +} + +TEST(TopologySort, Full_graph_with_loops) { + std::vector> graph = {{0, 1, 2}, {0, 1, 2}, {0, 1, 2}}; + std::vector ans = {0, 1, 2}; + ASSERT_EQ(top_sort(graph), ans); +} + +TEST(TopologySort, BIK_graph) { + std::vector> graph = {{1, 2, 3}, {5}, {5}, {4, 5}, {6, 8}, + {4, 6, 7}, {9}, {9}, {7, 9}, {}}; + std::vector ans = {0, 3, 2, 1, 5, 4, 8, 7, 6, 9}; + ASSERT_EQ(top_sort(graph), ans); +} + +TEST(TopologySort, Isolated_verticles) { + std::vector> graph = {{1, 2, 3}, {0}, {1}, {2}, {}, {4}}; + std::vector res = {5, 4, 0, 3, 2, 1}; + ASSERT_EQ(top_sort(graph), res); +} \ No newline at end of file diff --git a/task_01/src/topology_sort.cpp b/task_01/src/topology_sort.cpp index e53f670..578972c 100644 --- a/task_01/src/topology_sort.cpp +++ b/task_01/src/topology_sort.cpp @@ -1 +1,28 @@ #include "topology_sort.hpp" + +#include + +void dfs(int v, std::vector &used, std::vector &result, + std::vector > &graph) { + used[v] = true; + for (int i = 0; i < graph[v].size(); ++i) { + int to = graph[v][i]; + if (!used[to]) dfs(to, used, result, graph); + } + + result.push_back(v); +} + +std::vector top_sort(std::vector > &graph) { + std::vector used; + used.resize(graph.size()); + + std::vector result; + for (int i = 0; i < graph.size(); ++i) { + if (!used[i]) dfs(i, used, result, graph); + } + + reverse(result.begin(), result.end()); + + return result; +} \ No newline at end of file diff --git a/task_01/src/topology_sort.hpp b/task_01/src/topology_sort.hpp index 6f70f09..1d0c09f 100644 --- a/task_01/src/topology_sort.hpp +++ b/task_01/src/topology_sort.hpp @@ -1 +1,7 @@ #pragma once +#include + +void dfs(int v, std::vector &used, std::vector &result, + std::vector > &graph); + +std::vector top_sort(std::vector > &graph); \ No newline at end of file diff --git a/task_02/src/AllFunc.cpp b/task_02/src/AllFunc.cpp new file mode 100644 index 0000000..299840c --- /dev/null +++ b/task_02/src/AllFunc.cpp @@ -0,0 +1,25 @@ +#include "AllFunc.hpp" + +void AllFunc(std::vector > &graph, + std::set > &result, std::set &cpvector) { + int timer = 0, parent = -1; + + std::vector tin(graph.size(), 0); + std::vector fup(graph.size(), 0); + std::vector used(graph.size(), 0); + + std::set > res; + + std::set cpvec; + + for (int i = 0; i < graph.size(); i++) { + if (!used[i]) { + FindBridges(timer, i, graph, tin, fup, parent, res, used); + } + } + result = returnBridges(res); + tin.resize(graph.size(), 0); + fup.resize(graph.size(), 0); + used.resize(graph.size(), 0); + FindCutPoint(timer, 0, graph, tin, fup, parent, cpvec, used); +} \ No newline at end of file diff --git a/task_02/src/AllFunc.hpp b/task_02/src/AllFunc.hpp new file mode 100644 index 0000000..4ef9f19 --- /dev/null +++ b/task_02/src/AllFunc.hpp @@ -0,0 +1,7 @@ +#pragma once + +#include "FindBridges.hpp" +#include "FindCutPoints.hpp" + +void AllFunc(std::vector > &graph, + std::set > &result, std::set &cpvector); \ No newline at end of file diff --git a/task_02/src/FindBridges.cpp b/task_02/src/FindBridges.cpp new file mode 100644 index 0000000..116a18a --- /dev/null +++ b/task_02/src/FindBridges.cpp @@ -0,0 +1,27 @@ +#include "FindBridges.hpp" + +void FindBridges(int &timer, int start, std::vector > &graph, + std::vector &tin, std::vector &fup, int parent, + std::set > &result, + std::vector used) { + used[start] = 1; + tin[start] = fup[start] = ++timer; + int to; + for (int i = 0; i < graph[start].size(); ++i) { + to = graph[start][i]; + if (to == parent) continue; + if (used[to]) + fup[start] = std::min(fup[start], tin[to]); + else { + FindBridges(timer, to, graph, tin, fup, start, result, used); + fup[start] = std::min(fup[start], fup[to]); + if (fup[to] > tin[start]) + result.insert({std::min(start, to), std::max(start, to)}); + } + } +} + +std::set > returnBridges( + std::set > &result) { + return result; +} \ No newline at end of file diff --git a/task_02/src/FindBridges.hpp b/task_02/src/FindBridges.hpp new file mode 100644 index 0000000..09ce986 --- /dev/null +++ b/task_02/src/FindBridges.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include +#include +#include +#include + +void FindBridges(int &timer, int start, std::vector > &graph, + std::vector &tin, std::vector &fup, int parent, + std::set > &result, + std::vector used); + +std::set > returnBridges( + std::set > &result); \ No newline at end of file diff --git a/task_02/src/FindCutPoints.cpp b/task_02/src/FindCutPoints.cpp new file mode 100644 index 0000000..1fbaa2d --- /dev/null +++ b/task_02/src/FindCutPoints.cpp @@ -0,0 +1,24 @@ +#include "FindCutPoints.hpp" + +void FindCutPoint(int &timer, int start, std::vector > &graph, + std::vector &tin, std::vector &fup, int parent, + std::set &cpvec, std::vector used) { + used[start] = 1; + tin[start] = fup[start] = ++timer; + int to; + int children = 0; + for (int i = 0; i < graph[start].size(); ++i) { + to = graph[start][i]; + if (to == parent) continue; + if (used[to]) + fup[start] = std::min(fup[start], tin[to]); + else { + FindCutPoint(timer, to, graph, tin, fup, start, cpvec, used); + fup[start] = std::min(fup[start], fup[to]); + if (fup[to] >= tin[start] && parent != -1) cpvec.insert(start); + children++; + } + } +} + +std::set returnCP(std::set &cpvec) { return cpvec; } \ No newline at end of file diff --git a/task_02/src/FindCutPoints.hpp b/task_02/src/FindCutPoints.hpp new file mode 100644 index 0000000..b958c51 --- /dev/null +++ b/task_02/src/FindCutPoints.hpp @@ -0,0 +1,12 @@ +#pragma once + +#include +#include +#include +#include + +void FindCutPoint(int &timer, int start, std::vector > &graph, + std::vector &tin, std::vector &fup, int parent, + std::set &cpvec, std::vector used); + +std::set returnCP(std::set &cpvec); \ No newline at end of file diff --git a/task_02/src/main.cpp b/task_02/src/main.cpp index 0e4393b..7159b03 100644 --- a/task_02/src/main.cpp +++ b/task_02/src/main.cpp @@ -1,3 +1,4 @@ -#include +#include "FindBridges.hpp" +#include "FindCutPoints.hpp" -int main() { return 0; } +int main() {} \ No newline at end of file diff --git a/task_02/src/test.cpp b/task_02/src/test.cpp index 5e11617..8921c21 100644 --- a/task_02/src/test.cpp +++ b/task_02/src/test.cpp @@ -1,6 +1,72 @@ #include -TEST(TopologySort, Simple) { - ASSERT_EQ(1, 1); // Stack [] +#include "AllFunc.hpp" +#include "FindBridges.hpp" +#include "FindCutPoints.hpp" + +TEST(Bridges_and_CutPoints, Simple_Bridges) { + std::vector > graph = {{0, 1}, {1, 0}}; + std::set > result; + std::set cpvector; + AllFunc(graph, result, cpvector); + std::set > k = {{0, 1}}; + ASSERT_EQ(returnBridges(result), k); +} + +TEST(Bridges_and_CutPoints, Simple_CutPoints) { + std::vector > graph = {{0, 1}, {1, 0}}; + std::set > result; + std::set cpvector; + AllFunc(graph, result, cpvector); + std::set k = {}; + ASSERT_EQ(returnCP(cpvector), k); +} + +TEST(Bridges_and_CutPoints, Harder_Bridges) { + std::vector > graph = {{1, 2}, {0, 2, 3}, {0, 1}, + {1, 4, 5}, {3, 5}, {3, 4}}; + std::set > result; + std::set cpvector; + AllFunc(graph, result, cpvector); + std::set > k = {{1, 3}}; + ASSERT_EQ(returnBridges(result), k); +} + +TEST(Bridges_and_CutPoints, Harder_CutPoints_ale) { + std::vector > graph = {{1, 2}, {0, 2, 3}, {0, 1}, + {1, 4, 5}, {3, 5}, {3, 4}}; + std::vector tin(graph.size()); + std::vector fup(graph.size()); + std::vector used(graph.size()); + int tm = 0; + std::set > result; + std::set cpvector; + FindCutPoint(tm, 0, graph, tin, fup, -1, cpvector, used); + std::set k = {1, 3}; + ASSERT_EQ(returnCP(cpvector), k); +} + +TEST(Bridges_and_CutPoints, Most_hard_Bridges) { + std::vector > graph = { + {1, 3}, {0, 2, 3}, {1, 3}, {0, 1, 2, 4, 5, 6}, {3, 5}, {3, 4, 6}, {3, 5}}; + std::set > result; + std::set cpvector; + AllFunc(graph, result, cpvector); + std::set > k = {}; + ASSERT_EQ(returnBridges(result), k); +} + +TEST(Bridges_and_CutPoints, Most_hard_CutPoints) { + std::vector > graph = { + {1, 3}, {0, 2, 3}, {1, 3}, {0, 1, 2, 4, 5, 6}, {3, 5}, {3, 4, 6}, {3, 5}}; + std::vector tin(graph.size()); + std::vector fup(graph.size()); + std::vector used(graph.size()); + int tm = 0; + std::set > result; + std::set cpvector; + FindCutPoint(tm, 0, graph, tin, fup, -1, cpvector, used); + std::set k = {3}; + ASSERT_EQ(returnCP(cpvector), k); } diff --git a/task_03/src/Deijkstra.cpp b/task_03/src/Deijkstra.cpp new file mode 100644 index 0000000..a2cc7d2 --- /dev/null +++ b/task_03/src/Deijkstra.cpp @@ -0,0 +1,37 @@ +#include "Deijkstra.hpp" + +#include + +const int INF = 1e9; + +std::vector Deijkstra( + int start, std::vector > > &graph) { + if (start < 0) { + throw "Bad Input"; + } + + if (graph.size() == 0) throw "Empty Graph"; + + std::vector dist(graph.size(), INF); + dist[start] = 0; + std::vector used(graph.size()); + + for (int i = 0; i < graph.size(); ++i) { + int closest = -1; + for (int j = 0; j < graph.size(); ++j) { + if (!used[j] && (closest == -1 || dist[closest] > dist[j])) { + closest = j; + } + } + + used[closest] = true; + + for (auto &[to, weight] : graph[closest]) { + if (weight < 0) throw "Negative Weight!"; + if (dist[to] > dist[closest] + weight) { + dist[to] = dist[closest] + weight; + } + } + } + return dist; +} \ No newline at end of file diff --git a/task_03/src/Deijkstra.hpp b/task_03/src/Deijkstra.hpp new file mode 100644 index 0000000..b0850d9 --- /dev/null +++ b/task_03/src/Deijkstra.hpp @@ -0,0 +1,5 @@ +#pragma once +#include + +std::vector Deijkstra( + int start, std::vector > > &graph); \ No newline at end of file diff --git a/task_03/src/Johnson.cpp b/task_03/src/Johnson.cpp new file mode 100644 index 0000000..a706b58 --- /dev/null +++ b/task_03/src/Johnson.cpp @@ -0,0 +1,52 @@ +#include "Johnson.hpp" + +#include "Deijkstra.hpp" + +std::vector> Johnson(std::vector &edges, int m, int n) { + std::vector vec(n, INF); + std::vector tmp_edges = edges; + int v = 0; + vec[v] = 0; + for (int i = 0; i < n; ++i) { + tmp_edges.push_back({n, i, 0}); + } + + m += n; + + std::vector p(n, -1); + int x; + for (int i = 0; i < n; ++i) { + x = -1; + for (int j = 0; j < m; ++j) { + if (vec[tmp_edges[j].a] < INF) { + if (vec[tmp_edges[j].b] > vec[tmp_edges[j].a] + tmp_edges[j].cost) { + vec[tmp_edges[j].b] = std::min( + vec[tmp_edges[j].b], vec[tmp_edges[j].a] + tmp_edges[j].cost); + p[tmp_edges[j].b] = tmp_edges[j].a; + x = tmp_edges[j].b; + } + } + } + } + if (x != -1) { + throw "Negative Cycle"; + } + + for (int i = 0; i < edges.size(); ++i) { + edges[i].cost = edges[i].cost + vec[edges[i].a] - vec[edges[i].b]; + } + std::vector> res; + + std::vector>> graph; + graph.resize(n); + + for (int i = 0; i < edges.size(); ++i) { + graph[edges[i].a].push_back({edges[i].b, edges[i].cost}); + } + + for (int i = 0; i < n; ++i) { + res.push_back(Deijkstra(i, graph)); + } + + return res; +} \ No newline at end of file diff --git a/task_03/src/Johnson.hpp b/task_03/src/Johnson.hpp new file mode 100644 index 0000000..078373c --- /dev/null +++ b/task_03/src/Johnson.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include + +#include "Deijkstra.hpp" + +const int INF = 1e9; + +class edge { + public: + int a; + int b; + int cost; +}; + +std::vector> Johnson(std::vector &edges, int m, int n); \ No newline at end of file diff --git a/task_03/src/main.cpp b/task_03/src/main.cpp index 0e4393b..e6faa61 100644 --- a/task_03/src/main.cpp +++ b/task_03/src/main.cpp @@ -1,3 +1,7 @@ #include +#include -int main() { return 0; } +#include "Deijkstra.hpp" +#include "Johnson.hpp" + +int main() {} \ No newline at end of file diff --git a/task_03/src/test.cpp b/task_03/src/test.cpp index 5e11617..4a90e84 100644 --- a/task_03/src/test.cpp +++ b/task_03/src/test.cpp @@ -1,6 +1,42 @@ - #include -TEST(TopologySort, Simple) { - ASSERT_EQ(1, 1); // Stack [] +#include "Deijkstra.hpp" +#include "Johnson.hpp" + +TEST(Jonson, Test1) { + std::vector edges = {{0, 1, -1}, {0, 2, 4}, {1, 2, 3}, {1, 3, 2}, + {1, 4, 2}, {3, 2, 5}, {3, 1, 1}, {4, 3, -3}}; + std::vector> d = Johnson(edges, 8, 5); + std::vector> result = { + {0, 1, 2, 1, 1}, + {1000000000, 0, 1, 0, 0}, + {1000000000, 1000000000, 0, 1000000000, 1000000000}, + {1000000000, 0, 1, 0, 0}, + {1000000000, 0, 1, 0, 0}}; + ASSERT_EQ(d, result); } + +TEST(Jonson, NegativeCycle) { + std::vector edges = {{0, 5, 1}, {1, 0, 1}, {2, 1, 1}, {2, 3, 1}, + {3, 4, 1}, {4, 5, 1}, {5, 5, -10}, {5, 2, 3}}; + ASSERT_ANY_THROW(Johnson(edges, 8, 5)); +} + +TEST(Jonson, Test2) { + std::vector edges = {{0, 1, 1}, {0, 2, -2}, {1, 3, 10}, {3, 6, 1}, + {1, 6, -3}, {6, 2, 5}, {2, 5, -1}, {2, 4, 2}, + {5, 4, 2}, {6, 7, 10}, {5, 7, -3}}; + std::vector> d = Johnson(edges, 11, 8); + std::vector> result = { + {0, 1, 0, 11, 0, 0, 1, 0}, + {1000000000, 0, 4, 10, 4, 4, 0, 4}, + {1000000000, 1000000000, 0, 1000000000, 0, 0, 1000000000, 0}, + {1000000000, 1000000000, 8, 0, 8, 8, 4, 8}, + {1000000000, 1000000000, 1000000000, 1000000000, 0, 1000000000, + 1000000000, 1000000000}, + {1000000000, 1000000000, 1000000000, 1000000000, 0, 0, 1000000000, 0}, + {1000000000, 1000000000, 4, 1000000000, 4, 4, 0, 4}, + {1000000000, 1000000000, 1000000000, 1000000000, 1000000000, 1000000000, + 1000000000, 0}}; + ASSERT_EQ(d, result); +} \ No newline at end of file diff --git a/task_04/src/Deijkstra.cpp b/task_04/src/Deijkstra.cpp new file mode 100644 index 0000000..38a76fa --- /dev/null +++ b/task_04/src/Deijkstra.cpp @@ -0,0 +1,38 @@ +#include "Deijkstra.hpp" + +#include + +const int INF = 1e9; + +int Deijkstra(int start, int end, + std::vector > > &graph) { + if (end >= graph.size() or start < 0) { + throw "Bad Input"; + } + + if (graph.size() == 0) throw "Empty Graph"; + + std::vector dist(graph.size(), INF); + dist[start] = 0; + std::vector used(graph.size()); + + for (int i = 0; i < graph.size(); ++i) { + int closest = -1; + for (int j = 0; j < graph.size(); ++j) { + if (!used[j] && (closest == -1 || dist[closest] > dist[j])) { + closest = j; + } + } + + used[closest] = true; + + for (auto &[to, weight] : graph[closest]) { + if (weight < 0) throw "Negative Weight!"; + if (dist[to] > dist[closest] + weight) { + dist[to] = dist[closest] + weight; + } + } + } + if (dist[end] == INF) throw "No path to this verticle!"; + return dist[end]; +} \ No newline at end of file diff --git a/task_04/src/Deijkstra.hpp b/task_04/src/Deijkstra.hpp new file mode 100644 index 0000000..6c0fff4 --- /dev/null +++ b/task_04/src/Deijkstra.hpp @@ -0,0 +1,5 @@ +#pragma once +#include + +int Deijkstra(int start, int end, + std::vector > > &graph); \ No newline at end of file diff --git a/task_04/src/main.cpp b/task_04/src/main.cpp index 0e4393b..5913ffd 100644 --- a/task_04/src/main.cpp +++ b/task_04/src/main.cpp @@ -1,3 +1,8 @@ #include +#include -int main() { return 0; } +#include "Deijkstra.hpp" + +using namespace std; + +int main() {} \ No newline at end of file diff --git a/task_04/src/test.cpp b/task_04/src/test.cpp index 5e11617..627afa9 100644 --- a/task_04/src/test.cpp +++ b/task_04/src/test.cpp @@ -1,6 +1,109 @@ - #include -TEST(TopologySort, Simple) { - ASSERT_EQ(1, 1); // Stack [] +#include "Deijkstra.hpp" + +TEST(Deijkstra, Test_default_graph_1) { + std::vector > > graph = {{{1, 1}, {8, 1}}, + {{2, 1}}, + {{4, 1}}, + {{2, 1}, {4, 1}}, + {}, + {{7, 1}}, + {{5, 1}, {7, 1}}, + {{2, 1}, {4, 1}}, + {{6, 1}, {9, 1}}, + {{6, 1}}}; + ASSERT_EQ(Deijkstra(0, 4, graph), 3); } + +TEST(Deijkstra, Test_default_graph_2) { + std::vector > > graph = { + {{1, 10}, {2, 7}}, {{4, 3}, {3, 9}}, + {{3, 11}, {5, 2}}, {{6, 8}, {7, 12}, {8, 1}}, + {{6, 4}}, {{8, 5}}, + {{9, 6}}, {{9, 13}}, + {{9, 8}}, {}}; + ASSERT_EQ(Deijkstra(0, 9, graph), 22); +} + +TEST(Deijkstra, Test_default_graph_3) { + std::vector > > graph = { + {{1, 2}, {2, 2}, {5, 3}}, + {{2, 1}, {3, 2}, {6, 8}}, + {{3, 1}, {5, 4}}, + {{6, 3}, {4, 2}}, + {{5, 4}, {7, 6}}, + {{7, 7}}, + {{7, 8}}, + {}}; + ASSERT_EQ(Deijkstra(0, 7, graph), 10); +} + +TEST(Deijkstra, Test_default_graph_4) { + std::vector > > graph = { + {{1, 10}, {3, 8}, {4, 31}}, + {{2, 7}}, + {{3, 9}, {7, 27}}, + {{5, 11}}, + {{6, 32}}, + {{6, 17}, {8, 15}}, + {{8, 21}}, + {{8, 15}}, + {}}; + ASSERT_EQ(Deijkstra(0, 3, graph), 8); +} + +TEST(Deijkstra, Test_check_for_bad_input_1) { + std::vector > > graph = { + {{1, 10}, {2, 7}}, {{4, 3}, {3, 9}}, + {{3, 11}, {5, 2}}, {{6, 8}, {7, 12}, {8, 1}}, + {{6, 4}}, {{8, 5}}, + {{9, 6}}, {{9, 13}}, + {{9, 8}}, {}}; + ASSERT_ANY_THROW(Deijkstra(0, 10, graph)); +} + +TEST(Deijkstra, Test_check_for_bad_input_2) { + std::vector > > graph = { + {{1, 10}, {2, 7}}, {{4, 3}, {3, 9}}, + {{3, 11}, {5, 2}}, {{6, 8}, {7, 12}, {8, 1}}, + {{6, 4}}, {{8, 5}}, + {{9, 6}}, {{9, 13}}, + {{9, 8}}, {}}; + ASSERT_ANY_THROW(Deijkstra(-6, 9, graph)); +} + +TEST(Deijkstra, Test_empty_graph) { + std::vector > > graph = {}; + ASSERT_ANY_THROW(Deijkstra(0, 9, graph)); +} + +TEST(Deijkstra, Test_default_graph_with_negative_weights) { + std::vector > > graph = {{{1, 1}, {8, 1}}, + {{2, 1}}, + {{4, 1}}, + {{2, 1}, {4, -1}}, + {}, + {{7, 1}}, + {{5, -1}, {7, 1}}, + {{2, 1}, {4, 1}}, + {{6, 1}, {9, -1}}, + {{6, -1}}}; + ASSERT_ANY_THROW(Deijkstra(0, 9, graph)); +} + +TEST(Deijkstra, Test_for_isolated_verticle) { + std::vector > > graph = {{{1, 1}, {8, 1}}, + {{2, 1}}, + {{4, 1}}, + {{2, 1}, {4, -1}}, + {}, + {{7, 1}}, + {{5, -1}, {7, 1}}, + {{2, 1}, {4, 1}}, + {{6, 1}, {9, -1}}, + {{6, -1}}, + {}, + {}}; + ASSERT_ANY_THROW(Deijkstra(0, 10, graph)); +} \ No newline at end of file diff --git a/task_05/src/RMQ.cpp b/task_05/src/RMQ.cpp new file mode 100644 index 0000000..57ffbb0 --- /dev/null +++ b/task_05/src/RMQ.cpp @@ -0,0 +1,38 @@ +#include "RMQ.hpp" + +const int INF = 1e9; + +std::vector > table_construct(int n, std::vector &vec) { + int new_size = ceil(log2(n)); + for (int i = n; i < pow(2, new_size); ++i) { + vec.push_back(INF); + } + int tmp = 1; + int count = 1; + std::vector > vec2D; + vec2D.resize(pow(2, new_size)); + + std::vector tmp_vec = vec; + while (pow(2, tmp) <= pow(2, new_size)) { + for (int i = 0; i < tmp_vec.size() - count; ++i) { + vec2D[tmp].push_back(std::min(tmp_vec[i], tmp_vec[i + count])); + } + tmp_vec = vec2D[tmp]; + tmp++; + count *= 2; + } + + return vec2D; +} + +int RMQ(int a, int b, std::vector > vec2D) { + if (a > b or a < 0 or b >= vec2D.size()) throw "Bad input"; + if (vec2D.size() == 0) throw "Empty array"; + int dist = b - a + 1; + int log_dist = floor(log2(dist)); + + int k = + std::min(vec2D[log_dist][a], vec2D[log_dist][dist - pow(2, log_dist)]); + + return k; +} \ No newline at end of file diff --git a/task_05/src/RMQ.hpp b/task_05/src/RMQ.hpp new file mode 100644 index 0000000..5ded0b9 --- /dev/null +++ b/task_05/src/RMQ.hpp @@ -0,0 +1,8 @@ +#pragma once +#include +#include +#include + +std::vector > table_construct(int n, std::vector &vec); + +int RMQ(int a, int b, std::vector > vec2D); \ No newline at end of file diff --git a/task_05/src/main.cpp b/task_05/src/main.cpp index 0e4393b..aff3954 100644 --- a/task_05/src/main.cpp +++ b/task_05/src/main.cpp @@ -1,3 +1,26 @@ +#include #include +#include -int main() { return 0; } +#include "RMQ.hpp" + +int main() { + int n; + std::cin >> n; + + std::vector vec; + + int tmp; + + for (int i = 0; i < n; ++i) { + std::cin >> tmp; + vec.push_back(tmp); + } + + std::vector > vec2D = table_construct(n, vec); + + int a, b; + std::cin >> a >> b; + + int k = RMQ(a, b, vec2D); +} \ No newline at end of file diff --git a/task_05/src/test.cpp b/task_05/src/test.cpp index 5e11617..0421bf8 100644 --- a/task_05/src/test.cpp +++ b/task_05/src/test.cpp @@ -1,6 +1,34 @@ #include -TEST(TopologySort, Simple) { - ASSERT_EQ(1, 1); // Stack [] +#include + +TEST(RMQ, Test1) { + std::vector vec = {1, 0, 4, 3, 5, 10}; + std::vector > vec2D = table_construct(vec.size(), vec); + ASSERT_EQ(RMQ(1, 7, vec2D), 0); +} + +TEST(RMQ, Test2) { + std::vector vec = {14, 11, 13, 9, 7, 4, 10, 2, 5, 3}; + std::vector > vec2D = table_construct(vec.size(), vec); + ASSERT_EQ(RMQ(1, 8, vec2D), 2); +} + +TEST(RMQ, Test3) { + std::vector vec = {25, 16, 31, 50, 5, 21}; + std::vector > vec2D = table_construct(vec.size(), vec); + ASSERT_EQ(RMQ(4, 6, vec2D), 5); +} + +TEST(RMQ, Test_check_for_bad_input) { + std::vector vec = {14, 11, 13, 9, 7, 4, 10, 2, 5, 3}; + std::vector > vec2D = table_construct(vec.size(), vec); + ASSERT_ANY_THROW(RMQ(-1, 8, vec2D)); } + +TEST(RMQ, Empty_vector) { + std::vector vec = {}; + std::vector > vec2D = table_construct(vec.size(), vec); + ASSERT_ANY_THROW(RMQ(1, 8, vec2D)); +} \ No newline at end of file diff --git a/task_06/src/LCA.cpp b/task_06/src/LCA.cpp new file mode 100644 index 0000000..6202da8 --- /dev/null +++ b/task_06/src/LCA.cpp @@ -0,0 +1,77 @@ +#include "LCA.hpp" + +std::vector lca_h, lca_dfs_list, lca_first, lca_tree; +std::vector lca_dfs_used; + +bool Empty = false; +bool OnlyRoot = false; + +void lca_dfs(const graph &graph, int v, int h) { + lca_dfs_used[v] = 1; + lca_h[v] = h; + lca_dfs_list.push_back(v); + for (const_graph_iter i = graph[v].begin(); i != graph[v].end(); ++i) { + if (!lca_dfs_used[*i]) { + lca_dfs(graph, *i, h + 1); + lca_dfs_list.push_back(v); + } + } +} + +void lca_build_tree(int i, int l, int r) { + if (l == r) + lca_tree[i] = lca_dfs_list[l]; + else { + int m = (l + r) >> 1; + lca_build_tree(i + i, l, m); + lca_build_tree(i + i + 1, m + 1, r); + if (lca_h[lca_tree[i + i]] < lca_h[lca_tree[i + i + 1]]) + lca_tree[i] = lca_tree[i + i]; + else + lca_tree[i] = lca_tree[i + i + 1]; + } +} + +void lca_prepare(const graph &graph, int root) { + if (graph.empty()) { + Empty = true; + } else { + if (graph.size() == 1) { + OnlyRoot = true; + } else { + int n = (int)graph.size(); + lca_h.resize(n); + lca_dfs_list.reserve(n * 2); + lca_dfs_used.assign(n, 0); + + lca_dfs(graph, root); + + int m = (int)lca_dfs_list.size(); + lca_tree.assign(lca_dfs_list.size() * 4 + 1, -1); + lca_build_tree(1, 0, m - 1); + + lca_first.assign(n, -1); + for (int i = 0; i <= m; ++i) { + int v = lca_dfs_list[i]; + if (lca_first[v] == -1) lca_first[v] = i; + } + } + } +} + +int lca_tree_min(int i, int sl, int sr, int l, int r) { + if (sl == l && sr == r) return lca_tree[i]; + int sm = (sl + sr) >> 1; + if (r <= sm) return lca_tree_min(i + i, sl, sm, l, r); + if (l > sm) return lca_tree_min(i + i + 1, sm + 1, sr, l, r); + int ans1 = lca_tree_min(i + i, sl, sm, l, sm); + int ans2 = lca_tree_min(i + i + 1, sm + 1, sr, sm + 1, r); + return lca_h[ans1] < lca_h[ans2] ? ans1 : ans2; +} + +int lca(int a, int b) { + if (Empty or OnlyRoot) throw "Error"; + int left = lca_first[a], right = lca_first[b]; + if (left > right) std::swap(left, right); + return lca_tree_min(1, 0, (int)lca_dfs_list.size() - 1, left, right); +} \ No newline at end of file diff --git a/task_06/src/LCA.hpp b/task_06/src/LCA.hpp new file mode 100644 index 0000000..3391406 --- /dev/null +++ b/task_06/src/LCA.hpp @@ -0,0 +1,17 @@ +#pragma once + +#include +#include + +typedef std::vector> graph; +typedef std::vector::const_iterator const_graph_iter; + +void lca_dfs(const graph &graph, int v, int h = 1); + +void lca_build_tree(int i, int l, int r); + +void lca_prepare(const graph &graph, int root); + +int lca_tree_min(int i, int sl, int sr, int l, int r); + +int lca(int a, int b); \ No newline at end of file diff --git a/task_06/src/main.cpp b/task_06/src/main.cpp index 0e4393b..bc147b2 100644 --- a/task_06/src/main.cpp +++ b/task_06/src/main.cpp @@ -1,3 +1,23 @@ -#include +#include "LCA.hpp" -int main() { return 0; } +int main() { + int n, m, root; + std::cin >> n >> m; + + graph g; + g.resize(n); + + int from, to; + + for (int i = 0; i < m; ++i) { + std::cin >> from >> to; + g[from].push_back(to); + g[to].push_back(from); + } + std::cin >> root; + lca_prepare(g, root); + + int v1, v2; + std::cin >> v1 >> v2; + std::cout << lca(v1, v2); +} \ No newline at end of file diff --git a/task_06/src/test.cpp b/task_06/src/test.cpp index 5e11617..68b8e43 100644 --- a/task_06/src/test.cpp +++ b/task_06/src/test.cpp @@ -1,6 +1,42 @@ #include -TEST(TopologySort, Simple) { - ASSERT_EQ(1, 1); // Stack [] +#include "LCA.hpp" + +TEST(LCA, Simple) { + graph g = {{1, 2}, {0}, {0}}; + lca_prepare(g, 0); + ASSERT_EQ(lca(1, 2), 0); +} + +TEST(LCA, Difiicult) { + graph g = {{1, 2}, {0, 3, 4}, {0, 5}, {1}, {1, 6, 7}, {2, 8}, {4}, {4}, {5}}; + lca_prepare(g, 0); + ASSERT_EQ(lca(3, 7), 1); +} + +TEST(LCA, Difiicult_2) { + graph g = {{1, 2}, {0, 3, 4}, {0, 5}, {1}, {1, 6, 7}, {2, 8}, {4}, {4}, {5}}; + lca_prepare(g, 0); + ASSERT_EQ(lca(1, 8), 0); } + +TEST(LCA, Hard) { + graph g = {{1, 14}, {0, 2, 3}, {1, 4, 5}, {1, 6, 7}, {2}, + {2}, {3, 9, 8}, {3, 10, 13}, {6}, {6}, + {7, 11, 12}, {10}, {10}, {7}, {1}}; + lca_prepare(g, 0); + ASSERT_EQ(lca(9, 13), 3); +} + +TEST(LCA, Empty) { + graph g = {}; + lca_prepare(g, 0); + ASSERT_ANY_THROW(lca(1, 4)); +} + +TEST(LCA, Only_Root) { + graph g = {{}}; + lca_prepare(g, 0); + ASSERT_ANY_THROW(lca(1, 4)); +} \ No newline at end of file