Мне удалось O (n log n).
Вот (несколько интенсивная) реализация C ++:
#include <boost/pending/disjoint_sets.hpp>
#include <boost/property_map/property_map.hpp>
#include <map>
#include <set>
#include <iostream>
typedef std::map<int, int> rank_t;
typedef std::map<int, int> parent_t;
typedef boost::associative_property_map< rank_t > rank_pmap_t;
typedef boost::associative_property_map< parent_t > parent_pmap_t;
typedef boost::disjoint_sets< rank_pmap_t, parent_pmap_t > group_sets_t;
typedef std::set<int> key_set;
typedef std::map<int, std::set<int> > output;
С некоторыми определениями типов, вот настоящее мясо. Я использую boost :: disjoint_sets , что является исключительно хорошим представлением проблемы. Эта первая функция проверяет, видели ли какие-либо из указанных ключей ранее, и добавляет их в коллекции при необходимости. важная часть действительно union_set(a, b)
, которая связывает два набора вместе. Если один или другой набор уже находится в коллекции groups
, он также будет связан.
void add_data(int a, int b, group_sets_t & groups, key_set & keys)
{
if (keys.count(a) < 1) groups.make_set(a);
if (keys.count(b) < 1) groups.make_set(b);
groups.union_set(a, b);
keys.insert(a);
keys.insert(b);
}
Это не слишком захватывающе, оно просто перебирает все ключи, которые мы видели, и получает репрезентативный ключ для этого ключа, а затем добавляет пару (представитель, ключ) на карту. Как только это будет сделано, распечатайте карту.
void build_output(group_sets_t & groups, key_set & keys)
{
output out;
for (key_set::iterator i(keys.begin()); i != keys.end(); i++)
out[groups.find_set(*i)].insert(*i);
for (output::iterator i(out.begin()); i != out.end(); i++)
{
std::cout << i->first << ": ";
for (output::mapped_type::iterator j(i->second.begin()); j != i->second.end(); j++)
std::cout << *j << " ";
std::cout << std::endl;
}
}
int main()
{
rank_t rank;
parent_t parent;
rank_pmap_t rank_index(rank);
parent_pmap_t parent_index(parent);
group_sets_t groups( rank_index, parent_index );
key_set keys;
int a, b;
while (std::cin >> a)
{
std::cin >> b;
add_data(a, b, groups, keys);
}
build_output(groups, keys);
//std::cout << "number of sets: " <<
// groups.count_sets(keys.begin()), keys.end()) << std::endl;
}
Я не спал много часов, изучая, как использовать boost::disjoint_sets
для решения этой проблемы. Там, кажется, не так много документации по этому вопросу.
О спектакле. Структура disjoint_sets
- это O (& alpha; (n)) для ее ключевых операций (make_set
, find_set
и union_set
), которая довольно близка к постоянной, и поэтому, если бы это было просто вопросом построения структуры весь алгоритм был бы O (n & alpha; (n)) (что фактически равно O (n)), но мы должны распечатать его. Это означает, что мы должны создать несколько ассоциативных контейнеров, которые не могут работать лучше, чем O (n log n). Может быть возможно получить постоянное ускорение коэффициента, выбрав различные ассоциативные контейнеры (скажем, hash_set
и т. Д.), Поскольку после заполнения начального списка вы можете зарезервировать оптимальный объем пространства.