E039. Minimum spanning tree. Kruskal's algorithm

e-maxx algorithm original: C/C++ #algorithm #emaxx #graph #tree
선택한 UI 언어에 맞게 문제 텍스트를 러시아어에서 번역합니다. 코드는 변경하지 않습니다.

Источник: e-maxx.ru/algo, страница PDF 122.

Дан взвешенный неориентированный 그래프. it is required find такое под트리 этого 그래프а, которое бы соединяло все его вершины, и при этом обладало наименьшим весом (т.е. суммой весов рёбер) из всех возможных. Такое под트리 называется минимальным остовным 트리м или простом минимальным остовом. Здесь будут рассмотрены несколько важных фактов, связанных с минимальными остовами, затем будет рассмотрен Kruskal's algorithm в его простейшей реализации.

Свойства минимального остова

● minimum остов уникален, если веса всех рёбер различны. В противном случае,

может существовать несколько минимальных остовов (конкретные 알고리즘ы обычно получают один из возможных остовов).

● minimum остов является также и остовом с минимальным произведением весов рёбер.

(доказывается это легко, достаточно заменить веса всех рёбер на их логарифмы)

● minimum остов является также и остовом с минимальным весом самого тяжелого ребра.

(это утверждение следует из справедливости 알고리즘а Крускала)

● Остов максимального веса ищется аналогично остову минимального веса, достаточно поменять знаки

всех рёбер на противоположные и выполнить любой из 알고리즘 минимального остова.

Kruskal's algorithm

Данный 알고리즘 был описан Крускалом (Kruskal) в 1956 г. Kruskal's algorithm изначально помещает каждую вершину в своё 트리, а затем постепенно объединяет эти деревья, объединяя на каждой итерации два некоторых дерева некоторым edgeм. Перед началом выполнения 알고리즘а, все рёбра сортируются по весу (в порядке неубывания). Затем начинается процесс объединения: перебираются все рёбра от первого до последнего (в порядке сортировки), и если у текущего ребра его концы принадлежат разным поддеревьям, то эти поддеревья объединяются, а edge добавляется к ответу. По окончании перебора всех рёбер все вершины окажутся принадлежащими одному поддереву, и ответ найден.

Простейшая 구현

Этот код самым непосредственным образом реализует описанный выше 알고리즘, и выполняется за O (M log N + N2). Сортировка рёбер потребует O (M log N) операций. Принадлежность вершины тому или иному поддереву хранится просто с помощью 배열а tree_id - в нём для каждой вершины хранится номер дерева, которому она принадлежит. Для каждого ребра мы за O (1) определяем, принадлежат ли его концы разным деревьям. Наконец, объединение двух деревьев осуществляется за O (N) простым проходом по 배열у 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;

} }

Улучшенная 구현

С использованием структуры данных "Disjoint set union" можно написать более быструю реализацию 알고리즘а Крускала с асимптотикой O (M log N).

C# 해법

자동 초안, 제출 전 검토
using 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++ 해법

매칭됨/원본
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());
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 해법

자동 초안, 제출 전 검토
import 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;
            }
    }
}

Материал разбит как 알고리즘ическая 문제: изучить постановку, понять асимптотику и реализовать 알고리즘 на выбранном языке.

Vacancies for this task

활성 채용 with overlapping task tags are 표시됨.

전체 채용
아직 활성 채용이 없습니다.