Рубрики

Центроид Разложение Дерева

Фон :
Что такое центроид дерева?
Центроид дерева — это узел, который, если его удалить из дерева, разделит его на «лес», так что любое дерево в лесу будет иметь не более половины числа вершин в исходном дереве.

Предположим, что в дереве n узлов. «Размер поддерева» для узла — это размер дерева с корнем в узле.

   
Let S(v) be size of subtree rooted at node v

   S(v) = 1 + ? S(u) 

Here u is a child to v (adjacent and at a depth one 
greater than the depth of v).  

Centroid is a node v such that,

maximum(n - S(v), S(u1, S(u2, .. S(um) <= n/2

where ui is i'th child to v.

Нахождение центроида
Пусть T — неориентированное дерево с n узлами. Выберите любой произвольный узел v в дереве. Если v удовлетворяет математическому определению центроида, у нас есть наш центроид. Иначе мы знаем, что наше математическое неравенство не выполнялось, и из этого мы заключаем, что существует некоторое u, смежное с v, такое, что S (u)> n / 2. Мы делаем это с нашим новым v и рекурсом.

Мы никогда не возвращаемся к узлу, потому что, когда мы решили перейти от него к узлу с размером поддерева больше, чем n / 2, мы как бы объявили, что он теперь принадлежит компоненту с узлами меньше, чем n / 2, и мы никогда не найдем наш центроид там.
В любом случае мы движемся к центроиду. Кроме того, в дереве есть конечное число вершин. Процесс должен остановиться, и он остановится в нужной вершине.

Алгоритм:

  1. Выберите произвольный узел v
  2. Запустите DFS из v и настройте размеры поддеревьев
  3. Переместите в узел v (или начните с любого произвольного v, принадлежащего дереву)
  4. Проверьте математическое состояние центроида для v
    1. Если условие выполнено, вернуть текущий узел как центроид
    2. В противном случае перейдите к соседнему узлу с «наибольшим» размером поддерева и вернитесь к шагу 4.

Теорема: если дано дерево с n узлами, центроид всегда существует.
Доказательство. Из нашего подхода к проблеме ясно, что мы всегда можем найти центроид, используя описанные выше шаги.

Сложность времени

  1. Выберите произвольный узел v: O (1)
  2. DFS: O (n)
  3. Перестановка в v: O (1)
  4. Найти центроид: O (n)

Разложение по центроиду:

Нахождение центроида для дерева является частью того, что мы пытаемся достичь здесь. Нам нужно подумать, как мы можем организовать дерево в структуру, которая уменьшает сложность ответа на определенный «тип» запросов.

Алгоритм

  1. Сделать центроид в качестве корня нового дерева (которое мы назовем «центроидное дерево»)
  2. Рекурсивно разложить деревья в полученном лесу
  3. Сделайте центроиды этих деревьев детьми центроидов, которые в последний раз их разделяли.

Дерево центроидов имеет глубину O (lg n) и может быть построено в O (n lg n), так как мы можем найти центроид в O (n).

Наглядный пример
Рассмотрим дерево с 16 узлами. На рисунке уже заданы размеры поддеревьев с использованием DFS из узла 1.

Мы начинаем с узла 1 и видим, выполняется ли условие для центроида. Помните, что S (v) — это размер поддерева для v.

Мы делаем узел 6 в качестве корня нашего центроида и рекурсируем по 3 деревьям лесного центроида, разделив исходное дерево на.

ПРИМЕЧАНИЕ . На рисунке поддеревья, сгенерированные центроидом, были обведены пунктирной линией того же цвета, что и цвет центроида.

Мы делаем впоследствии найденные центроиды дочерними по отношению к центроидам, которые разделяют их последними, и получаем наше дерево центроидов

ПРИМЕЧАНИЕ . Деревья, содержащие только один элемент, имеют тот же элемент, что и их центроид. Мы не использовали цветовое дифференцирование для таких деревьев, и узлы листьев представляют их.

// C ++ программа для центроидной декомпозиции дерева
#include <bits/stdc++.h>

using namespace std;

  
#define MAXN 1025

  

vector<int> tree[MAXN];

vector<int> centroidTree[MAXN];

bool centroidMarked[MAXN];

  
/ * метод добавления ребра между узлами неориентированного дерева * /

void addEdge(int u, int v)

{

    tree[u].push_back(v);

    tree[v].push_back(u);

}

  
/ * метод для настройки размеров поддеревьев и узлов в текущем дереве * /

void DFS(int src, bool visited[], int subtree_size[], int* n)

{

    / * отметить посещенный узел * /

    visited[src] = true;

  

    / * увеличить количество посещенных узлов * /

    *n += 1;

  

    / * инициализировать размер поддерева для текущего узла * /

    subtree_size[src] = 1;

  

    vector<int>::iterator it;

  

    / * возвращаемся к непосещенным и нецентроидным соседям * /

    for (it = tree[src].begin(); it!=tree[src].end(); it++)

        if (!visited[*it] && !centroidMarked[*it])

        {

            DFS(*it, visited, subtree_size, n);

            subtree_size[src]+=subtree_size[*it];

        }

}

  

int getCentroid(int src, bool visited[], int subtree_size[], int n)

{

    / * предположим, что текущий узел является центроидом * /

    bool is_centroid = true;

  

    / * отметить его как посещенный * /

    visited[src] = true;

  

    / * отслеживать самый тяжелый потомок узла, чтобы использовать в случае, если узел

       не центроид * /

    int heaviest_child = 0;

  

    vector<int>::iterator it;

  

    / * перебирать все смежные узлы, которые являются дочерними

       (не посещен) и не отмечен как центроид для некоторых

       поддерево * /

    for (it = tree[src].begin(); it!=tree[src].end(); it++)

        if (!visited[*it] && !centroidMarked[*it])

        {

            / * Если любой соседний узел имеет более n / 2 узлов,

             * текущий узел не может быть центроидом * /

            if (subtree_size[*it]>n/2)

                is_centroid=false;

  

            / * обновить самого тяжелого ребенка * /

            if (heaviest_child==0 ||

                subtree_size[*it]>subtree_size[heaviest_child])

                heaviest_child = *it;

        }

  

    / * если текущий узел - центроид * /

    if (is_centroid && n-subtree_size[src]<=n/2)

        return src;

  

    / * еще вернемся к тяжелому ребенку * /

    return getCentroid(heaviest_child, visited, subtree_size, n);

}

  
/ * функция для получения центроида дерева с корнем в src.

 * дерево может быть исходным или принадлежать лесу * /

int getCentroid(int src)

{

    bool visited[MAXN];

  

    int subtree_size[MAXN];

  

    / * инициализировать вспомогательные массивы * /

    memset(visited, false, sizeof visited);

    memset(subtree_size, 0, sizeof subtree_size);

  

    / * переменная для хранения количества узлов в текущем дереве * /

    int n = 0;

  

    / * DFS для настройки размеров поддеревьев и узлов в текущем дереве * /

    DFS(src, visited, subtree_size, &n);

  

    for (int i=1; i<MAXN; i++)

        visited[i] = false;

  

    int centroid = getCentroid(src, visited, subtree_size, n);

  

    centroidMarked[centroid]=true;

  

    return centroid;

}

  
/ * функция для генерации дерева центроидов дерева с корнем в src * /

int decomposeTree(int root)

{

    // printf ("degposeTree (% d) / n", root);

  

    / * получить sentorid для текущего дерева * /

    int cend_tree = getCentroid(root);

  

    printf("%d ", cend_tree);

  

    vector<int>::iterator it;

  

    / * для каждого узла, смежного с найденным центроидом

     * и еще не отмечен как центроид * /

    for (it=tree[cend_tree].begin(); it!=tree[cend_tree].end(); it++)

    {

        if (!centroidMarked[*it])

        {

            / * разложить поддерево с корнем в соседнем узле * /

            int cend_subtree = decomposeTree(*it);

  

            / * добавить ребро между центроидом дерева и центроидом поддерева * /

            centroidTree[cend_tree].push_back(cend_subtree);

            centroidTree[cend_subtree].push_back(cend_tree);

        }

    }

  

    / * вернуть центроид дерева * /

    return cend_tree;

}

  
// функция драйвера

int main()

{

    / * количество узлов в дереве * /

    int n = 16;

  

    / * аргументы в порядке: узел u, узел v

     * последовательность начинается с 1 * /

    addEdge(1, 4);

    addEdge(2, 4);

    addEdge(3, 4);

    addEdge(4, 5);

    addEdge(5, 6);

    addEdge(6, 7);

    addEdge(7, 8);

    addEdge(7, 9);

    addEdge(6, 10);

    addEdge(10, 11);

    addEdge(11, 12);

    addEdge(11, 13);

    addEdge(12, 14);

    addEdge(13, 15);

    addEdge(13, 16);

  

    / * генерирует дерево центроидов * /

    decomposeTree(1);

  

    return 0;

}

Выход :

6 4 1 2 3 5 7 8 9 11 10 12 14 13 15 16

Заявка:

Рассмотрим ниже пример проблемы

 Если дано взвешенное дерево с N узлами, найдите минимальное число
ребер в пути длины K или возврат -1, если такой путь
не существует.
1

Решение грубой силы: для каждого узла выполните DFS, чтобы найти расстояние и количество ребер до каждого другого узла

Сложность по времени: O (N 2 ) Очевидно, неэффективно, потому что N = 200000

Мы можем решить вышеуказанную проблему за O (N Log N), используя Centroid Decomposition .

  1. Выполните центроидную декомпозицию, чтобы получить «дерево поддеревьев»
  2. Начните с корня разложения, решите задачу для каждого поддерева следующим образом
    1. Решите проблему для каждого «дочернего дерева» текущего поддерева.
    2. Выполните DFS от центроида на текущем поддереве, чтобы вычислить минимальное число ребер для путей, которые включают центроид
      1. Два случая: центр тяжести в конце или в середине пути
      2. Используйте массив с временными метками размером 1000000, чтобы отслеживать, какие расстояния от центроида возможны, и минимальное количество ребер для этого расстояния.

  3. Возьмите минимум двух выше

Временная сложность решения на основе центроидного разложения составляет O (n log n)

Ссылка :
http://www.ugrad.cs.ubc.ca/~cs490/2014W2/pdf/jason.pdf

Эта статья предоставлена Яшем Варяни . Если вам нравится GeeksforGeeks и вы хотите внести свой вклад, вы также можете написать статью и отправить ее по почте на contrib@geeksforgeeks.org. Смотрите свою статью, появляющуюся на главной странице GeeksforGeeks, и помогите другим вундеркиндам.

Пожалуйста, напишите комментарии, если вы обнаружите что-то неправильное, или вы хотите поделиться дополнительной информацией по обсуждаемой теме

Рекомендуемые посты:

Центроид Разложение Дерева

0.00 (0%) 0 votes