diff --git a/additional_tasks/template_task/src/utils.cpp b/additional_tasks/template_task/src/utils.cpp index 0ee624c..0c58fd6 100644 --- a/additional_tasks/template_task/src/utils.cpp +++ b/additional_tasks/template_task/src/utils.cpp @@ -1 +1 @@ -#include "utils.hpp" +#include "utils.hpp" \ No newline at end of file diff --git a/additional_tasks/template_task/src/utils.hpp b/additional_tasks/template_task/src/utils.hpp index 6f70f09..7b9637e 100644 --- a/additional_tasks/template_task/src/utils.hpp +++ b/additional_tasks/template_task/src/utils.hpp @@ -1 +1 @@ -#pragma once +#pragma once \ No newline at end of file diff --git a/lib/src/Graph.hpp b/lib/src/Graph.hpp new file mode 100644 index 0000000..c2c4833 --- /dev/null +++ b/lib/src/Graph.hpp @@ -0,0 +1,284 @@ +#include +#include + +using namespace std; + +class NegativeCycleGraph : public std::logic_error { + using std::logic_error::logic_error; +}; + +struct Edge { + int from, to, weight; // Представляет ребро графа с начальной и конечной + // вершинами и весом. +}; + +struct Graph { + vector edges; // Этот вектор нужен в реализации алгоритма Беллмана + // форда + vector> gr; // Список смежности для невзвешенного графа. + vector>> + directed_graph; // Список смежности для взвешенного ориентированного + // графа. + vector used; // Массив для отслеживания посещенных вершин. + vector color; // Массив для отслеживания состояния вершин (0 - не + // посещена, 1 - в процессе, 2 - посещена). + vector parents; // Массив для хранения родителей вершин в дереве обхода. + vector + topsort; // Массив для хранения порядка топологической сортировки. + vector> comps; // Массив для хранения компонент связности. + vector dist; // Массив для хранения расстояний от начальной вершины. + int cnt = 0; // Счетчик компонент связности. + + void ReadWUG(int& n, int& m, vector>& vec) { + // Читает взвешенный неориентированный граф из вектора vec. + directed_graph.resize(n + 1); // Инициализация списка смежности. + used.resize(n + 1, 0); // Инициализация массива посещенных вершин. + color.resize(n + 1, 0); // Инициализация массива цветов. + parents.resize(n + 1, -1); // Инициализация массива родителей. + comps.resize(n + 1); // Инициализация массива компонент. + for (int i = 0; i < m; i++) { + int u, v, w; // Начальная, конечная вершины и вес ребра. + u = vec[i][0]; + v = vec[i][1]; + w = vec[i][2]; + directed_graph[u].push_back( + {v, w}); // Добавление ребра в список смежности. + directed_graph[v].push_back({u, w}); // Добавление обратного ребра. + } + } + + void ReadWDG(int& n, int& m, vector>& vec) { + // Читает взвешенный ориентированный граф из вектора vec. + directed_graph.resize(n + 1); + used.resize(n + 1, 0); + color.resize(n + 1, 0); + parents.resize(n + 1, -1); + comps.resize(n + 1); + for (int i = 0; i < m; i++) { + int u, v, w; + u = vec[i][0]; + v = vec[i][1]; + w = vec[i][2]; + directed_graph[u].push_back( + {v, w}); // Добавление ребра в список смежности. + } + } + + void ReadUUG(int& n, int& m, vector>& vec) { + // Читает невзвешенный неориентированный граф из вектора vec. + gr.resize(n + 1); + used.resize(n + 1, 0); + comps.resize(n + 1); + for (int i = 0; i < m; i++) { + int u, v; + u = vec[i].first; + v = vec[i].second; + gr[u].push_back(v); // Добавление ребра в список смежности. + gr[v].push_back(u); // Добавление обратного ребра. + } + } + + void ReadUDG(int& n, int& m, vector>& vec) { + // Читает невзвешенный ориентированный граф из вектора vec. + gr.resize(n + 1); + used.resize(n + 1, 0); + color.resize(n + 1, 0); + parents.resize(n + 1, -1); + comps.resize(n + 1); + for (int i = 0; i < m; i++) { + int u, v; + u = vec[i].first; + v = vec[i].second; + gr[u].push_back(v); // Добавление ребра в список смежности. + } + } + + void Dfs(int u, int count) { + // Выполняет обход в глубину (DFS) от вершины u и сохраняет компоненты + // связности. + used[u] = 1; // Помечаем вершину как посещенную. + comps[count].push_back(u); // Добавляем вершину в текущую компоненту. + for (auto it : gr[u]) { + if (!used[it]) { + Dfs(it, count); // Рекурсивный вызов для соседней вершины. + } + } + topsort.push_back( + u); // Добавляем вершину в порядок топологической сортировки. + } + + vector> components(int& n) { + // Находит все компоненты связности в графе. + for (int i = 1; i <= n; i++) { + if (!used[i]) { + Dfs(i, cnt++); // Запускаем DFS для каждой непосещенной вершины. + } + } + return comps; // Возвращаем найденные компоненты. + } + + bool HasCycleUndirected(int& n) { + // Проверяет наличие циклов в невзвешенном неориентированном графе. + for (int i = 1; i <= n; i++) { + if (!used[i]) { + if (DfsCycleUndirected(i, -1)) { + return true; // Если цикл найден, возвращаем true. + } + } + } + return false; // Циклов не найдено. + } + bool DfsCycleUndirected(int v, int p) { + // Выполняет DFS для проверки наличия циклов в невзвешенном графе. + used[v] = 1; // Помечаем вершину как посещенную. + for (const int to : gr[v]) { + if (!used[to]) { + if (DfsCycleUndirected(to, v)) { + return true; // Если цикл найден, возвращаем true. + }; + } else if (to != p) { + return true; // Если соседняя вершина уже посещена и не является + // родителем, найден цикл. + } + } + return false; // Циклов не найдено. + } + + bool HasCycleDirected(int& n) { + // Проверяет наличие циклов в взвешенном ориентированном графе. + for (int i = 1; i <= n; i++) { + if (!used[i]) { + if (DfsCycleDirectedW(i)) { + return true; // Если цикл найден, возвращаем true. + } + } + } + return false; // Циклов не найдено. + } + + bool DfsCycleDirectedW(int v) { + // Выполняет DFS для проверки наличия циклов в ориентированном графе. + used[v] = 1; // Помечаем вершину как посещенную. + color[v] = 1; // Устанавливаем цвет в процессе. + for (auto& edge : directed_graph[v]) { + int const to = edge.first; // Получаем соседнюю вершину. + if (!used[to]) { + if (DfsCycleDirectedW(to)) { + return true; // Если цикл найден, возвращаем true. + } + } else if (color[to] == 1) { + return true; // Если соседняя вершина в процессе, найден цикл. + } + } + color[v] = 2; // Устанавливаем цвет завершенной. + return false; // Циклов не найдено. + } + + // Используется для нахождения Top_Sort и проверке на циклы + bool DfsCycleDirectedU(const int v) { + // Выполняет DFS для топологической сортировки и проверки наличия циклов. + used[v] = 1; // Помечаем вершину как посещенную. + color[v] = 1; // Устанавливаем цвет в процессе. + for (auto& edge : gr[v]) { + int const to = edge; // Получаем соседнюю вершину. + if (!used[to]) { + if (DfsCycleDirectedU(to)) { + return true; // Если цикл найден, возвращаем true. + } + } else if (color[to] == 1) { + return true; // Если соседняя вершина в процессе, найден цикл. + } + } + color[v] = 2; // Устанавливаем цвет завершенной. + topsort.push_back( + v); // Добавляем вершину в порядок топологической сортировки. + return false; // Циклов не найдено. + } + + // Реализация Алгоритма Флойда, на вход передается матрица (в виде двумерного + // массива), возвращает массив пар, в которой содержится пара вершин между + // которыми ориентированное ребро и его вес + vector, int>> AlgotihmFloida(int n, + vector>& vec) { + // Тройной цикл для перебора всех пар вершин (i, j) с промежуточной вершиной + // k + for (int k = 0; k < n; k++) { + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + // Проверяем, что i и j не совпадают + if (i != j) { + // Обновляем расстояние между вершинами i и j, если найдено более + // короткое + vec[i][j] = min(vec[i][j], vec[i][k] + vec[k][j]); + } + } + } + } + + // Вектор для хранения результатов: пар (i, j) и их расстояний + vector, int>> ans; + // Перебираем все пары вершин для формирования результата + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + // Проверяем, что расстояние не равно бесконечности и вершины i и j не + // совпадают + if (vec[i][j] != 1e9 && i != j) { + // Добавляем пару (i, j) и соответствующее расстояние в ответ + ans.push_back({{i, j}, vec[i][j]}); + } + } + } + + // Возвращаем вектор с кратчайшими путями + return ans; + } + + // Ниже реализован алгоритм Белмана Форда, который ищет кратчайшее расстояние + // между вершиной s и всеми остальными + vector AlgorithmBellmanaF(int n, int m, int s) { + edges.resize( + m); // Изменяем размер вектора рёбер на количество рёбер в графе + int k = 0; // Индекс для добавления рёбер + + // Заполнение списка рёбер + for (int i = 1; i <= n; i++) { + for (const auto& neighbor : directed_graph[i]) { + edges[k].from = i; // Устанавливаем начальную вершинуу + edges[k].to = neighbor.first; // Устанавливаем конечную вершину + edges[k].weight = neighbor.second; // Устанавливаем вес ребра + k++; // Переходим к следующему ребру + } + } + + vector distance( + n + 1, 1e9); // Инициализация расстояний до всех вершин бесконечностью + distance[s] = 0; // Расстояние до стартовой вершины равно 0 + + // Основной цикл алгоритма Беллмана-Форда + for (int i = 1; i <= n - 1; i++) { // Повторяем n-1 раз + for (const auto& edge : edges) { // Проходим по всем рёбрам + if (distance[edge.from] != + 1e9) { // Если до начальной вершины есть путь + distance[edge.to] = // Обновляем расстояние до конечной вершины + min(distance[edge.to], + distance[edge.from] + + edge.weight); // Сравниваем текущее расстояние и новое + } + } + } + + // Проверка на отрицательные циклы + for (const auto& edge : edges) { // Проходим по всем рёбрам еще раз + if (distance[edge.from] != 1e9 && // Если до начальной вершины есть путь + distance[edge.to] > + distance[edge.from] + + edge.weight) { // Если можно улучшить расстояние + throw NegativeCycleGraph( + "Error: Graph has negative cycle."); // Генерация исключения при + // обнаружении отрицательного + // цикла + } + } + return distance; // Возвращаем массив расстояний до всех вершин + } +}; diff --git a/task_01/src/main.cpp b/task_01/src/main.cpp index 76e8197..0e4393b 100644 --- a/task_01/src/main.cpp +++ b/task_01/src/main.cpp @@ -1 +1,3 @@ +#include + int main() { return 0; } diff --git a/task_01/src/test.cpp b/task_01/src/test.cpp index 87cef73..c1536a5 100644 --- a/task_01/src/test.cpp +++ b/task_01/src/test.cpp @@ -1,5 +1,108 @@ -#include +#include "gtest/gtest.h" +#include "top_sort.hpp" -TEST(Test, Simple) { - ASSERT_EQ(1, 1); // Stack [] -} \ No newline at end of file +TEST(SimpleGraph, Simple) { + int const vertices = 3; + int const edges = 2; + std::vector> edge = {{1, 2}, {2, 3}}; + + std::vector const result = TopSort(vertices, edges, edge); + std::vector const expected = {1, 2, 3}; + + ASSERT_EQ(result, expected); +} + +TEST(GraphWithCycle, Simple) { + int const vertices = 3; + int const edges = 3; + std::vector> edge = {{1, 2}, {2, 3}, {3, 1}}; + + EXPECT_THROW(TopSort(vertices, edges, edge);, CycleGraph); +} + +TEST(DisconnectedGraph, Simple) { + int const vertices = 4; + int const edges = 2; + std::vector> edge = {{1, 2}, {3, 4}}; + + std::vector const result = TopSort(vertices, edges, edge); + std::vector const expected = {3, 4, 1, 2}; + + ASSERT_EQ(result, expected); +} + +TEST(SingleVertex, Simple) { + int const vertices = 1; + int const edges = 0; + std::vector> edge = {}; + + std::vector const result = TopSort(vertices, edges, edge); + std::vector const expected = {1}; + + ASSERT_EQ(result, expected); +} + +TEST(MultipleValidOrders, Simple) { + int const vertices = 4; + int const edges = 3; + std::vector> edge = {{1, 2}, {1, 3}, {3, 4}}; + + std::vector const result = TopSort(vertices, edges, edge); + std::vector const expected1 = {1, 3, 4, 2}; + + ASSERT_EQ(result, expected1); +} + +TEST(AllVerticesConnected, Simple) { + int const vertices = 5; + int const edges = 4; + std::vector> edge = {{1, 2}, {2, 3}, {3, 4}, {4, 5}}; + + std::vector const result = TopSort(vertices, edges, edge); + std::vector const expected = {1, 2, 3, 4, 5}; + + ASSERT_EQ(result, expected); +} + +TEST(ReverseOrder, Simple) { + int const vertices = 3; + int const edges = 2; + std::vector> edge = {{3, 2}, {2, 1}}; + + std::vector const result = TopSort(vertices, edges, edge); + std::vector const expected = {3, 2, 1}; + + ASSERT_EQ(result, expected); +} + +TEST(ComplexGraph, Simple) { + int const vertices = 6; + int const edges = 6; + std::vector> edge = {{5, 2}, {5, 0}, {4, 0}, + {4, 1}, {2, 3}, {3, 1}}; + + std::vector const result = TopSort(vertices, edges, edge); + std::vector const expected = {6, 5, 4, 0, 2, 3, 1}; + + ASSERT_EQ(result, expected); +} + +TEST(GraphWithMultipleCyclesm, Simple) { + int const vertices = 4; + int const edges = 5; + std::vector> edge = { + {1, 2}, {2, 3}, {3, 1}, {2, 4}, {4, 2}}; + + EXPECT_THROW(TopSort(vertices, edges, edge), CycleGraph); +} + +TEST(MediumGraph, Simple) { + int const vertices = 6; + int const edges = 6; + std::vector> edge = {{5, 2}, {4, 2}, {3, 4}, + {6, 3}, {6, 1}, {5, 1}}; + std::vector const result = TopSort(vertices, edges, edge); + std::vector const expected = {6, 5, 3, 4, 2, 1}; + + ASSERT_EQ(result, expected); +} diff --git a/task_01/src/top_sort.cpp b/task_01/src/top_sort.cpp new file mode 100644 index 0000000..63b35f8 --- /dev/null +++ b/task_01/src/top_sort.cpp @@ -0,0 +1,20 @@ +#include "top_sort.hpp" + +#include "Graph.hpp" + +std::vector TopSort(int vertices, int edges, + std::vector>& edge) { + Graph graph_UDG; + graph_UDG.ReadUDG(vertices, edges, edge); + bool is_cycle = false; + for (int i = 1; i <= vertices; i++) { + if (!graph_UDG.used[i]) { + is_cycle = graph_UDG.DfsCycleDirectedU(i); + } + if (is_cycle) { + throw CycleGraph("Cycle found -> Topological sorting is not possible"); + } + } + reverse(graph_UDG.topsort.begin(), graph_UDG.topsort.end()); + return graph_UDG.topsort; +} \ No newline at end of file diff --git a/task_01/src/top_sort.hpp b/task_01/src/top_sort.hpp new file mode 100644 index 0000000..54bb799 --- /dev/null +++ b/task_01/src/top_sort.hpp @@ -0,0 +1,10 @@ +#pragma once +#include +#include +#include +class CycleGraph : public std::logic_error { + using std::logic_error::logic_error; +}; + +std::vector TopSort(int vertices, int edges, + std::vector>& edge); \ No newline at end of file diff --git a/task_02/src/bridges_and_point.cpp b/task_02/src/bridges_and_point.cpp new file mode 100644 index 0000000..6f0f33a --- /dev/null +++ b/task_02/src/bridges_and_point.cpp @@ -0,0 +1,108 @@ +#include "bridges_and_point.hpp" + +#include "Graph.hpp" + +using namespace std; + +vector> gr; +vector ret, h; +vector u; +vector used, tin, fup, used_br; +vector> answer; +int timer = 1; + +int one = 0; +set ans; + +void DfsCutPoint(int v, int p) { + if (p == -1) + h[v] = 0; + else + h[v] = h[p] + 1; + ret[v] = h[v]; + u[v] = true; + for (auto& to : gr[v]) { + if (to == p) continue; + if (u[to]) { + ret[v] = min(ret[v], h[to]); + } else { + if (p == -1) one++; + DfsCutPoint(to, v); + ret[v] = min(ret[v], ret[to]); + if (ret[to] >= h[v]) ans.insert(v); + } + } +} + +set CutPoint(int n, Graph graph) { + u.resize(n + 1, 0); + ret.resize(n + 1, 1e9); + h.resize(n + 1, 1e9); + gr = graph.gr; + u = graph.used; + for (int i = 1; i <= n; i++) { + if (u[i] == 0) { + one = 0; + DfsCutPoint(i, -1); + if (one <= 1) { + ans.erase(i); + } + } + } + return ans; +} + +void DfsCutBridges(int u, int p = -1) { + used_br[u] = 1; + tin[u] = fup[u] = timer++; + for (auto it : gr[u]) { + if (it == p) { + continue; + } + if (used_br[it]) { + fup[u] = min(fup[u], tin[it]); + } else { + DfsCutBridges(it, u); + fup[u] = min(fup[u], fup[it]); + if (fup[it] > tin[u]) { + answer.push_back({u, it}); + } + } + } +} + +vector> CutBridges(int n, Graph graph) { + tin.resize(n + 1, 0); + fup.resize(n + 1, 1e9); + used_br.resize(n + 1, 0); + gr = graph.gr; + used_br = graph.used; + for (int i = 1; i <= n; i++) { + if (!used_br[i]) { + DfsCutBridges(i); + } + } + + sort(answer.begin(), answer.end(), [](pair u, pair v) { + return u.first + u.second < v.first + v.second; + }); + + return answer; +} + +pair, vector>> Answer(int n, int m, + vector> vec) { + Graph graph; + graph.ReadUUG(n, m, vec); + vector> answer_bridges = CutBridges(n, graph); + set answer_point = CutPoint(n, graph); + u.clear(); + ret.clear(); + h.clear(); + used_br.clear(); + tin.clear(); + fup.clear(); + answer.clear(); + ans.clear(); + return {answer_point, answer_bridges}; +} \ No newline at end of file diff --git a/task_02/src/bridges_and_point.hpp b/task_02/src/bridges_and_point.hpp new file mode 100644 index 0000000..28d535f --- /dev/null +++ b/task_02/src/bridges_and_point.hpp @@ -0,0 +1,16 @@ +#pragma once +#include +#include +#include + +using namespace std; + +void DfsCutPoint(int v, int p); +void Dfs_cit_bridges(int u, int p = -1); + +set Cut_point(int n, int m, const vector> gr); +vector> Cut_bridges(int& n, int& m, + const vector> gr); + +pair, vector>> Answer(int n, int m, + vector> vec); \ No newline at end of file diff --git a/task_02/src/stack.cpp b/task_02/src/stack.cpp deleted file mode 100644 index 8ca8990..0000000 --- a/task_02/src/stack.cpp +++ /dev/null @@ -1,21 +0,0 @@ -#include "stack.hpp" - -#include - -void Stack::Push(int value) { data_.push(value); } - -int Stack::Pop() { - auto result = data_.top(); - data_.pop(); - return result; -} - -void MinStack::Push(int value) { data_.push_back(value); } - -int MinStack::Pop() { - auto result = data_.back(); - data_.pop_back(); - return result; -} - -int MinStack::GetMin() { return *std::min_element(data_.begin(), data_.end()); } \ No newline at end of file diff --git a/task_02/src/stack.hpp b/task_02/src/stack.hpp deleted file mode 100644 index 138ec40..0000000 --- a/task_02/src/stack.hpp +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include -#include - -class Stack { - public: - void Push(int value); - int Pop(); - - private: - std::stack data_; -}; - -class MinStack { - public: - void Push(int value); - int Pop(); - int GetMin(); - - private: - std::vector data_; -}; diff --git a/task_02/src/test.cpp b/task_02/src/test.cpp index 54e7ce9..5a59e95 100644 --- a/task_02/src/test.cpp +++ b/task_02/src/test.cpp @@ -1,42 +1,140 @@ +#include "bridges_and_point.hpp" +#include "gtest/gtest.h" -#include - -#include - -#include "stack.hpp" - -TEST(StackTest, Simple) { - Stack stack; - stack.Push(1); // Stack [1] - ASSERT_EQ(stack.Pop(), 1); // Stack [] - stack.Push(1); // Stack [1] - stack.Push(2); // Stack [1, 2] - ASSERT_EQ(stack.Pop(), 2); // Stack [1] - ASSERT_EQ(stack.Pop(), 1); // Stack [] - stack.Push(1); // Stack [1] - stack.Push(2); // Stack [1, 2] - ASSERT_EQ(stack.Pop(), 2); // Stack [1] - stack.Push(3); // Stack [1, 3] - ASSERT_EQ(stack.Pop(), 3); // Stack [1] - ASSERT_EQ(stack.Pop(), 1); // Stack [] -} - -TEST(MinStackTest, Simple) { - MinStack stack; - stack.Push(1); // Stack [1] - ASSERT_EQ(stack.GetMin(), 1); - ASSERT_EQ(stack.Pop(), 1); // Stack [] - stack.Push(1); // Stack [1] - stack.Push(2); // Stack [1, 2] - ASSERT_EQ(stack.GetMin(), 1); - ASSERT_EQ(stack.Pop(), 2); // Stack [1] - ASSERT_EQ(stack.Pop(), 1); // Stack [] - stack.Push(1); // Stack [1] - stack.Push(2); // Stack [1, 2] - ASSERT_EQ(stack.GetMin(), 1); - ASSERT_EQ(stack.Pop(), 2); // Stack [1] - stack.Push(3); // Stack [1, 3] - ASSERT_EQ(stack.GetMin(), 1); - ASSERT_EQ(stack.Pop(), 3); // Stack [1] - ASSERT_EQ(stack.Pop(), 1); // Stack [] +TEST(SimpleTest, Simple) { + int const vertices = 6; + int const edge = 6; + std::vector> const edges = {{1, 3}, {2, 3}, {3, 4}, + {3, 5}, {4, 5}, {4, 6}}; + + pair, vector>> const result = + Answer(vertices, edge, edges); + pair, vector>> const expected = { + {3, 4}, {{1, 3}, {3, 2}, {4, 6}}}; + ASSERT_EQ(result, expected); +} + +TEST(CutPointsAndBridgesTest, BasicFunctionality) { + int const vertices = 5; + int const edges = 5; + std::vector> const edgeList = { + {1, 2}, {2, 3}, {3, 4}, {4, 5}, {2, 5}}; + + pair, vector>> const result = + Answer(vertices, edges, edgeList); + set const expectedCutPoints = {2}; + vector> const expectedBridges = {{1, 2}}; + + ASSERT_EQ(result.first, expectedCutPoints); + ASSERT_EQ(result.second, expectedBridges); +} + +TEST(CutPointsAndBridgesTest, NoCutPoints) { + int const vertices = 4; + int const edges = 4; + std::vector> const edgeList = { + {1, 2}, {2, 3}, {3, 4}, {4, 1}}; + + pair, vector>> const result = + Answer(vertices, edges, edgeList); + set const expectedCutPoints = {}; + vector> const expectedBridges = {}; + + ASSERT_EQ(result.first, expectedCutPoints); + ASSERT_EQ(result.second, expectedBridges); +} + +TEST(CutPointsAndBridgesTest, ComplexGraph) { + int const vertices = 7; + int const edges = 7; + std::vector> const edgeList = { + {1, 2}, {1, 3}, {2, 3}, {3, 4}, {4, 5}, {5, 6}, {6, 7}}; + + pair, vector>> const result = + Answer(vertices, edges, edgeList); + set const expectedCutPoints = {3, 4, 5, 6}; + vector> const expectedBridges = { + {3, 4}, {4, 5}, {5, 6}, {6, 7}}; + + ASSERT_EQ(result.first, expectedCutPoints); + ASSERT_EQ(result.second, expectedBridges); +} + +TEST(CutPointsAndBridgesTest, TwoDisconnectedComponents) { + int const vertices = 6; + int const edges = 4; + std::vector> const edgeList = { + {1, 2}, {2, 3}, {4, 5}, {5, 6}}; + + pair, vector>> const result = + Answer(vertices, edges, edgeList); + set const expectedCutPoints = {2, 5}; + vector> const expectedBridges = { + {1, 2}, {2, 3}, {4, 5}, {5, 6}}; + + ASSERT_EQ(result.first, expectedCutPoints); + ASSERT_EQ(result.second, expectedBridges); +} + +TEST(CutPointsAndBridgesTest, StarTopology) { + int const vertices = 5; + int const edges = 4; + std::vector> const edgeList = { + {1, 2}, {1, 3}, {1, 4}, {1, 5}}; + + pair, vector>> const result = + Answer(vertices, edges, edgeList); + set const expectedCutPoints = {1}; + vector> const expectedBridges = { + {1, 2}, {1, 3}, {1, 4}, {1, 5}}; + + ASSERT_EQ(result.first, expectedCutPoints); + ASSERT_EQ(result.second, expectedBridges); +} + +TEST(CutPointsAndBridgesTest, CycleGraph) { + int const vertices = 5; + int const edges = 5; + std::vector> const edgeList = { + {1, 2}, {2, 3}, {3, 4}, {4, 5}, {5, 1}}; + + pair, vector>> const result = + Answer(vertices, edges, edgeList); + set const expectedCutPoints = {}; + vector> const expectedBridges = {}; + + ASSERT_EQ(result.first, expectedCutPoints); + ASSERT_EQ(result.second, expectedBridges); +} + +TEST(CutPointsAndBridgesTest, SingleBridgeInComplexGraph) { + int const vertices = 8; + int const edges = 8; + std::vector> const edgeList = { + {1, 2}, {2, 3}, {3, 4}, {4, 5}, {5, 6}, {6, 7}, {7, 8}, {5, 8}}; + + pair, vector>> const result = + Answer(vertices, edges, edgeList); + set const expectedCutPoints = {2, 3, 4, 5}; + vector> const expectedBridges = { + {1, 2}, {2, 3}, {3, 4}, {4, 5}}; + + ASSERT_EQ(result.first, expectedCutPoints); + ASSERT_EQ(result.second, expectedBridges); +} + +TEST(CutPointsAndBridgesTest, LargeGraph) { + int const vertices = 10; + int const edges = 9; + std::vector> const edgeList = { + {1, 2}, {1, 3}, {3, 4}, {4, 5}, {5, 6}, {6, 7}, {7, 8}, {8, 9}, {9, 10}}; + + pair, vector>> const result = + Answer(vertices, edges, edgeList); + set const expectedCutPoints = {1, 3, 4, 5, 6, 7, 8, 9}; + vector> const expectedBridges = { + {1, 2}, {1, 3}, {3, 4}, {4, 5}, {5, 6}, {6, 7}, {7, 8}, {8, 9}, {9, 10}}; + + ASSERT_EQ(result.first, expectedCutPoints); + ASSERT_EQ(result.second, expectedBridges); } \ No newline at end of file diff --git a/task_03/src/algorithm_Johnsona.cpp b/task_03/src/algorithm_Johnsona.cpp new file mode 100644 index 0000000..2275644 --- /dev/null +++ b/task_03/src/algorithm_Johnsona.cpp @@ -0,0 +1,14 @@ +#include + +using namespace std; + +void Jonson(int n, int m, vector>& arr_edges) { + int s = n + 1; + for (int i = 1; i <= n; i++) { + arr_edges.push_back({s, i, 0}); + } + int new_n = n + 1; + int new_m = m + n; + Graph graph; + graph.ReadWDG(new_n, new_m, arr_edges); +} diff --git a/task_03/src/topology_sort.hpp b/task_03/src/algorithm_Johnsona.hpp similarity index 100% rename from task_03/src/topology_sort.hpp rename to task_03/src/algorithm_Johnsona.hpp diff --git a/task_03/src/test.cpp b/task_03/src/test.cpp index ef5a86a..5e11617 100644 --- a/task_03/src/test.cpp +++ b/task_03/src/test.cpp @@ -1,8 +1,6 @@ #include -#include "topology_sort.hpp" - TEST(TopologySort, Simple) { ASSERT_EQ(1, 1); // Stack [] } diff --git a/task_03/src/topology_sort.cpp b/task_03/src/topology_sort.cpp deleted file mode 100644 index e53f670..0000000 --- a/task_03/src/topology_sort.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "topology_sort.hpp" diff --git a/task_04/src/algorithm_Dijkstra.cpp b/task_04/src/algorithm_Dijkstra.cpp new file mode 100644 index 0000000..826f50d --- /dev/null +++ b/task_04/src/algorithm_Dijkstra.cpp @@ -0,0 +1,52 @@ +#include "lib\src\Graph.hpp" +#include + +using namespace std; + +pair, vector> Dijkstra(int& n, int& m, int& s, int& t, + vector>& vec) { + Graph graph; + graph.ReadWUG(n, m, vec); + vector>> gr = graph.directed_graph; + vector put(n + 1, -1); + set> dist; + vector visited(n + 1, 0); + vector ans(n + 1, 1e9); + dist.emplace(0, s); + ans[s] = 0; + while (!dist.empty()) { + auto [d, u] = *dist.begin(); + visited[u] = 1; + dist.erase(dist.find(*dist.begin())); + ans[u] = d; + if (u == t) { + break; + } + for (auto [v, d1] : gr[u]) { + if (visited[v]) { + continue; + } + int new_d = d + d1; + if (new_d < ans[v]) { + if (dist.find({ans[v], v}) != dist.end()) { + dist.erase(dist.find({ans[v], v})); + } + ans[v] = new_d; + put[v] = u; + dist.emplace(new_d, v); + } + } + } + if (ans[t] == 1e9) { + throw Nonexistent_way("There is no path from point s to point t"); + } + vector way; + int const tu = t; + while (put[t] != -1) { + way.push_back(t); + t = put[t]; + } + way.push_back(s); + reverse(way.begin(), way.end()); + return make_pair(make_pair(ans[tu], way.size()), way); +} \ No newline at end of file diff --git a/task_04/src/algorithm_Dijkstra.hpp b/task_04/src/algorithm_Dijkstra.hpp new file mode 100644 index 0000000..aa1b7a0 --- /dev/null +++ b/task_04/src/algorithm_Dijkstra.hpp @@ -0,0 +1,13 @@ +#pragma once +#include +#include +#include +#include +using namespace std; + +class Nonexistent_way : public std::logic_error { + using std::logic_error::logic_error; +}; + +pair, vector> Dijkstra(int& n, int& m, int& s, int& t, + vector>& vec); \ No newline at end of file diff --git a/task_04/src/test.cpp b/task_04/src/test.cpp index 5e11617..b2a990b 100644 --- a/task_04/src/test.cpp +++ b/task_04/src/test.cpp @@ -1,6 +1,127 @@ - #include +#include + TEST(TopologySort, Simple) { - ASSERT_EQ(1, 1); // Stack [] + int n = 5; + int m = 7; + int s = 2; + int t = 5; + std::vector> vec = {{1, 2, 1}, {1, 3, 4}, {1, 5, 5}, {2, 3, 1}, + {3, 4, 2}, {3, 5, 3}, {4, 5, 7}}; + std::pair, std::vector> const expected = {{4, 3}, + {2, 3, 5}}; + std::pair, std::vector> const result = + Dijkstra(n, m, s, t, vec); + ASSERT_EQ(result, expected); } + +TEST(DijkstraTest, BasicPath) { + int n = 4; + int m = 5; + int s = 1; + int t = 4; + std::vector> vec = { + {1, 2, 1}, {1, 3, 4}, {2, 3, 2}, {2, 4, 5}, {3, 4, 1}}; + std::pair, std::vector> const expected = { + {4, 4}, {1, 2, 3, 4}}; + std::pair, std::vector> const result = + Dijkstra(n, m, s, t, vec); + ASSERT_EQ(result, expected); +} + +TEST(DijkstraTest, NoPath) { + int n = 3; + int m = 2; + int s = 1; + int t = 3; + std::vector> vec = {{1, 2, 2}, {2, 1, 2}}; + EXPECT_THROW(Dijkstra(n, m, s, t, vec), Nonexistent_way); +} + +TEST(DijkstraTest, MultiplePaths) { + int n = 6; + int m = 8; + int s = 1; + int t = 5; + std::vector> vec = {{1, 2, 1}, {1, 3, 2}, {2, 4, 3}, + {3, 4, 1}, {4, 5, 1}, {2, 5, 5}, + {3, 5, 3}, {1, 6, 10}}; + std::pair, std::vector> const expected = { + {4, 4}, {1, 3, 4, 5}}; + std::pair, std::vector> const result = + Dijkstra(n, m, s, t, vec); + ASSERT_EQ(result, expected); +} + +TEST(DijkstraTest, SingleNode) { + int n = 1; + int m = 0; + int s = 1; + int t = 1; + std::vector> vec = {}; + std::pair, std::vector> const expected = {{0, 1}, + {1}}; + std::pair, std::vector> const result = + Dijkstra(n, m, s, t, vec); + ASSERT_EQ(result, expected); +} + +TEST(DijkstraTest, CyclePath) { + int n = 4; + int m = 4; + int s = 1; + int t = 3; + std::vector> vec = { + {1, 2, 1}, {2, 3, 2}, {3, 1, 2}, {2, 4, 1}, {4, 3, 1}}; + std::pair, std::vector> const expected = {{2, 2}, + {1, 3}}; + std::pair, std::vector> const result = + Dijkstra(n, m, s, t, vec); + ASSERT_EQ(result, expected); +} + +TEST(DijkstraTest, LargeWeights) { + int n = 3; + int m = 3; + int s = 1; + int t = 3; + std::vector> vec = { + {1, 2, 1000}, {2, 3, 2000}, {1, 3, 5000}}; + std::pair, std::vector> const expected = {{3000, 3}, + {1, 2, 3}}; + std::pair, std::vector> const result = + Dijkstra(n, m, s, t, vec); + ASSERT_EQ(result, expected); +} + +TEST(DijkstraTest, ManyEdges) { + int n = 5; + int m = 10; + int s = 1; + int t = 5; + std::vector> vec = { + {1, 2, 1}, {1, 3, 2}, {1, 4, 5}, {2, 3, 1}, {2, 4, 2}, + {2, 5, 4}, {3, 4, 1}, {3, 5, 3}, {4, 5, 1}, {1, 5, 10}}; + std::pair, std::vector> const expected = { + {4, 4}, {1, 2, 4, 5}}; + std::pair, std::vector> const result = + Dijkstra(n, m, s, t, vec); + ASSERT_EQ(result, expected); +} + +TEST(DijkstraTest, ComplexGraph) { + int n = 6; + int m = 15; + int s = 1; + int t = 6; + std::vector> vec = { + {1, 2, 1}, {1, 3, 4}, {1, 4, 2}, {2, 3, 1}, {2, 4, 5}, + {3, 5, 2}, {4, 5, 3}, {4, 6, 1}, {5, 6, 1}, {2, 6, 10}, + {3, 4, 2}, {1, 5, 7}, {2, 5, 3}, {4, 3, 1}, {5, 1, 8}}; + std::pair, std::vector> const expected = {{3, 3}, + {1, 4, 6}}; + std::pair, std::vector> const result = + Dijkstra(n, m, s, t, vec); + ASSERT_EQ(result, expected); +} \ No newline at end of file