Алгоритм Краскала: различия между версиями
Перейти к навигации
Перейти к поиску
Ctrlalt (обсуждение | вклад) Нет описания правки |
Ctrlalt (обсуждение | вклад) Нет описания правки |
||
(не показана 1 промежуточная версия этого же участника) | |||
Строка 24: | Строка 24: | ||
int r1 = findRoot(v1), r2 = findRoot(v2); | int r1 = findRoot(v1), r2 = findRoot(v2); | ||
if (r1 == r2) | if (r1 == r2) | ||
return; | |||
if (rand() % 2) | if (rand() % 2) | ||
parent[r1] = r2; | parent[r1] = r2; | ||
Строка 49: | Строка 49: | ||
cin >> vertexCount >> edgeCount; | cin >> vertexCount >> edgeCount; | ||
vector<Edge> edges; | vector<Edge> edges(edgeCount); | ||
for (auto &[a, b, weight] : edges) { | for (auto &[a, b, weight] : edges) { | ||
cin >> a >> b >> weight; | cin >> a >> b >> weight; |
Текущая версия от 04:57, 13 января 2021
Сначала сортируем список рёбер по неубыванию весов.
Затем проходим по отсортированному списку рёбер и добавляем очередное ребро в остовное дерево, если его концы принадлежат разным компонентам связности. Для проверки этого факта используем систему непересекающихся множеств.
// Система непересекающихся множеств для быстрого определения, лежат ли концы ребра в разных компонентах связности struct DSU { vector<int> parent; DSU(int n) { for (int v = 0; v < n; v++) parent.push_back(v); } int findRoot(int v) { return parent[v] == v ? v : parent[v] = findRoot(parent[v]); } bool connected(int v1, int v2) { return findRoot(v1) == findRoot(v2); } void merge(int v1, int v2) { int r1 = findRoot(v1), r2 = findRoot(v2); if (r1 == r2) return; if (rand() % 2) parent[r1] = r2; else parent[r2] = r1; } };
// Структура для хранения взвешенного ребра // Рёбра сравниваются по весу, список рёбер будет сортироваться по неубыванию весов struct Edge { int a, b, weight; bool operator < (const Edge &that) const { return weight < that.weight; } };
// Считывание списка рёбер графа // (вершины во вводе нумеруются с 1, во внутреннем представлении — с 0) int vertexCount, edgeCount; cin >> vertexCount >> edgeCount; vector<Edge> edges(edgeCount); for (auto &[a, b, weight] : edges) { cin >> a >> b >> weight; a--; b--; }
// Алгоритм Краскала // (в данном примере определяется вес минимального остовного дерева — mstWeight) int mstWeight = 0; sort(edges.begin(), edges.end()); DSU dsu(vertexCount); for (auto &[a, b, weight] : edges) { if (!dsu.connected(a, b)) { dsu.merge(a, b); mstWeight += weight; } }
Сложность алгоритма Краскала:
- 1 раз выполняется сортировка списка рёбер за время O(ElogE);
- E раз выполняется запрос к системе непересекающихся множеств за O(1).
Итого сложность O(ElogE) = O(ElogV).
Для разреженных графов алгоритмы Прима и Краскала одинаково эффективны. Для плотных графов соответствующий вариант алгоритма Прима эффективнее, чем алгоритм Краскала.
Ссылки
Теория:
- e-maxx.ru — Алгоритм Крускала с системой непересекающихся множеств
- neerc.ifmo.ru/wiki — Алгоритм Краскала
- brestprog.by — Минимальное остовное дерево. Алгоритм Прима. Алгоритм Крускала
- algorithmica.org — Минимальные остовы
- usaco.guide — MST
- algs4.cs.princeton.edu/lectures — 4.3 Minimum Spanning Trees
Демонстрация:
Код: