E039. Minimum spanning tree. Kruskal's algorithm
Источник: e-maxx.ru/algo, страница PDF 122.
Дан взвешенный неориентированный grafo. it is required find такое подárvore этого grafoа, которое бы соединяло все его вершины, и при этом обладало наименьшим весом (т.е. суммой весов рёбер) из всех возможных. Такое подárvore называется минимальным остовным árvoreм или простом минимальным остовом. Здесь будут рассмотрены несколько важных фактов, связанных с минимальными остовами, затем будет рассмотрен Kruskal's algorithm в его простейшей реализации.
Свойства минимального остова
● minimum остов уникален, если веса всех рёбер различны. В противном случае,
может существовать несколько минимальных остовов (конкретные Algoritmoы обычно получают один из возможных остовов).
● minimum остов является также и остовом с минимальным произведением весов рёбер.
(доказывается это легко, достаточно заменить веса всех рёбер на их логарифмы)
● minimum остов является также и остовом с минимальным весом самого тяжелого ребра.
(это утверждение следует из справедливости Algoritmoа Крускала)
● Остов максимального веса ищется аналогично остову минимального веса, достаточно поменять знаки
всех рёбер на противоположные и выполнить любой из Algoritmo минимального остова.
Kruskal's algorithm
Данный Algoritmo был описан Крускалом (Kruskal) в 1956 г. Kruskal's algorithm изначально помещает каждую вершину в своё árvore, а затем постепенно объединяет эти деревья, объединяя на каждой итерации два некоторых дерева некоторым edgeм. Перед началом выполнения Algoritmoа, все рёбра сортируются по весу (в порядке неубывания). Затем начинается процесс объединения: перебираются все рёбра от первого до последнего (в порядке сортировки), и если у текущего ребра его концы принадлежат разным поддеревьям, то эти поддеревья объединяются, а edge добавляется к ответу. По окончании перебора всех рёбер все вершины окажутся принадлежащими одному поддереву, и ответ найден.
Простейшая Implementação
Этот код самым непосредственным образом реализует описанный выше Algoritmo, и выполняется за O (M log N + N2). Сортировка рёбер потребует O (M log N) операций. Принадлежность вершины тому или иному поддереву хранится просто с помощью arrayа tree_id - в нём для каждой вершины хранится номер дерева, которому она принадлежит. Для каждого ребра мы за O (1) определяем, принадлежат ли его концы разным деревьям. Наконец, объединение двух деревьев осуществляется за O (N) простым проходом по arrayу tree_id. given, что всего операций объединения будет N-1, мы и получаем асимптотику O (M log N + N2).
int m;
vector < pair < int, pair<int,int> > > g (m); // вес - vertex 1 - vertex 2
int cost = 0;
vector < pair<int,int> > res;
sort (g.begin(), g.end());
vector<int> tree_id (n);
for (int i=0; i<n; ++i)
tree_id[i] = i;
for (int i=0; i<m; ++i)
{
int a = g[i].second.first, b = g[i].second.second, l = g[i].first;
if (tree_id[a] != tree_id[b])
{
cost += l;
res.push_back (make_pair (a, b));
int old_id = tree_id[b], new_id = tree_id[a];
for (int j=0; j<n; ++j)
if (tree_id[j] == old_id)
tree_id[j] = new_id;
} }
Улучшенная Implementação
С использованием структуры данных "Disjoint set union" можно написать более быструю реализацию Algoritmoа Крускала с асимптотикой O (M log N).
C# solução
rascunho automático, revisar antes de enviarusing System;
using System.Collections.Generic;
using System.Linq;
public static class AlgorithmDraft
{
// Auto-generated C# draft from the original e-maxx C/C++ listing. Review before production use.
int m;
vector < pair < int, pair<int,int> > > g (m); // вес - вершина 1 - вершина 2
int cost = 0;
vector < pair<int,int> > res;
sort (g.begin(), g.end());
List<int> tree_id (n);
for (int i=0; i<n; ++i)
tree_id[i] = i;
for (int i=0; i<m; ++i)
{
int a = g[i].second.first, b = g[i].second.second, l = g[i].first;
if (tree_id[a] != tree_id[b])
{
cost += l;
res.push_back (make_pair (a, b));
int old_id = tree_id[b], new_id = tree_id[a];
for (int j=0; j<n; ++j)
if (tree_id[j] == old_id)
tree_id[j] = new_id;
}
}
}
C++ solução
correspondente/originalint m;
vector < pair < int, pair<int,int> > > g (m); // вес - вершина 1 - вершина 2
int cost = 0;
vector < pair<int,int> > res;
sort (g.begin(), g.end());
vector<int> tree_id (n);
for (int i=0; i<n; ++i)
tree_id[i] = i;
for (int i=0; i<m; ++i)
{
int a = g[i].second.first, b = g[i].second.second, l = g[i].first;
if (tree_id[a] != tree_id[b])
{
cost += l;
res.push_back (make_pair (a, b));
int old_id = tree_id[b], new_id = tree_id[a];
for (int j=0; j<n; ++j)
if (tree_id[j] == old_id)
tree_id[j] = new_id;
}
}
Java solução
rascunho automático, revisar antes de enviarimport java.util.*;
import java.math.*;
public class AlgorithmDraft {
// Auto-generated Java draft from the original e-maxx C/C++ listing. Review before production use.
int m;
vector < pair < int, pair<int,int> > > g (m); // вес - вершина 1 - вершина 2
int cost = 0;
vector < pair<int,int> > res;
sort (g.begin(), g.end());
ArrayList<Integer> tree_id (n);
for (int i=0; i<n; ++i)
tree_id[i] = i;
for (int i=0; i<m; ++i)
{
int a = g[i].second.first, b = g[i].second.second, l = g[i].first;
if (tree_id[a] != tree_id[b])
{
cost += l;
res.push_back (make_pair (a, b));
int old_id = tree_id[b], new_id = tree_id[a];
for (int j=0; j<n; ++j)
if (tree_id[j] == old_id)
tree_id[j] = new_id;
}
}
}
Материал разбит как Algoritmoическая Tarefa: изучить постановку, понять асимптотику и реализовать Algoritmo на выбранном языке.
Vacancies for this task
vagas ativas with overlapping task tags are mostradas.