Краскал: правильная реализация СНМ со сжатием путей и рангом для MST
Реализую минимальное остовное дерево алгоритмом Краскала. Сортирую рёбра по весу и добавляю, если они не образуют цикл — проверку цикла делаю через систему непересекающихся множеств (СНМ/DSU). Без оптимизаций СНМ работает медленно. Как правильно написать СНМ со сжатием путей и объединением по рангу, и какая итоговая сложность Краскала?
2 ответа
Краскал: сортируем рёбра по возрастанию веса, идём по ним и для каждого ребра (a,b) объединяем компоненты, если a и b ещё в разных. Цикл не образуется ровно тогда, когда find(a) != find(b).
Ключ к скорости — СНМ с двумя оптимизациями. Со сжатием путей + объединением по рангу амортизированная стоимость операции = O(α(n)) (обратная функция Аккермана, практически константа).
struct DSU {
vector<int> p, rnk;
DSU(int n): p(n), rnk(n, 0) { iota(p.begin(), p.end(), 0); }
int find(int x) { return p[x] == x ? x : p[x] = find(p[x]); } // сжатие
bool unite(int a, int b) {
a = find(a); b = find(b);
if (a == b) return false;
if (rnk[a] < rnk[b]) swap(a, b);
p[b] = a;
if (rnk[a] == rnk[b]) rnk[a]++;
return true;
}
};
long long kruskal(int n, vector<array<long long,3>> edges) { // {w,a,b}
sort(edges.begin(), edges.end());
DSU d(n);
long long cost = 0; int cnt = 0;
for (auto& [w, a, b] : edges)
if (d.unite(a, b)) { cost += w; if (++cnt == n - 1) break; }
return cost;
}
Сложность: O(E log E) на сортировку (доминирует) плюс O(E·α(V)) на операции СНМ. Память O(V+E).
Грабля: суммарный вес копите в long long — при E до 2·10^5 рёбер и весах до 10^9 сумма легко переполняет int.
Нюанс «когда MST уже собран»: можно прервать цикл, как только добавлено n-1 рёбер (в коде выше это if (++cnt == n-1) break;). Для связного графа это всегда достижимо; если граф несвязен, после прохода всех рёбер у вас будет минимальный остовный лес, а cnt < n-1.
Ещё про оптимизации СНМ: достаточно одной из двух (сжатие путей ИЛИ ранг) для O(log n) на операцию, но обе вместе дают O(α). На олимпиадах берите обе — пишется в 6 строк. И не путайте rank (высота дерева) с size (число элементов) — объединять можно по любому из них, главное прицеплять меньшее дерево к большему.