Количество различных путей в дереве, у которых значение узлов в этом пути больше или равно K

Постановка задачи:
Вам задано целое число N, обозначающее количество узлов в этом дереве. Теперь вам нужно подсчитать, сколько разных путей есть в дереве, чтобы минимальное значение узла в этом пути было больше или равно k.

Формат ввода:

  1. Первая строка содержит общее количество узлов N в этом дереве и положительное целое значение K.
  2. Следующие N-1 строк содержат пару целых чисел u, v (значения не разделены запятыми), которые означают, что в дереве есть ребро между узлами u и v.

Пример:

Ввод:

4 2  
1 2  
2 3  
3 4  

Ожидаемый результат:

3

Изменить: я не могу придумать, как подойти к этой проблеме. Итак, пожалуйста, дайте мне подсказку, чтобы я мог продолжить ее реализацию. Буду признателен даже за малейшую помощь.

Обновление:

1 <= N <= 10^5
1 <= u,v <= N  
1 <= K <= 10^6  

Наивный подход к такой проблеме не пройдет ни при каких обстоятельствах. Сложность решения должна быть либо O(n**2), либо O(nlogn).


person taurus05    schedule 10.02.2019    source источник
comment
@ גלעדברקן Я просто хочу подсказку, чтобы определить способ получения всех возможных путей от корневого узла к дочернему узлу, которые удовлетворяют заданному ограничению с наименьшей сложностью. Если для этого есть алгоритм, то я могу легко определить общее количество подпутей, используя nC2, где n — это общее количество узлов в этом отдельном пути.   -  person taurus05    schedule 10.02.2019
comment
Где в вопросе говорится, что от корневого узла до дочернего узла? Он просто говорит, разные пути... в дереве...   -  person גלעד ברקן    schedule 10.02.2019
comment
Да, ты прав. Но в дереве у вас есть иерархические отношения между узлами. Итак, я использовал термин root node to child node.   -  person taurus05    schedule 10.02.2019
comment
Рассмотрим дерево с 20 узлами. Всего на одном пути от начального узла к конечному узлу 5 узлов, и все они удовлетворяют заданному ограничению. Следовательно, можно легко сказать, что общее количество различных путей в этом маршруте будет равно 5C2.   -  person taurus05    schedule 10.02.2019
comment
Пожалуйста, используйте термин, отличный от корня, для описания начальной точки пути. Это слишком запутанно :) (Я не мог понять ваш предыдущий комментарий.) (И обращаюсь к модераторам - пожалуйста, прекратите перемещать комментарии в чат, когда они имеют отношение к собственно вопросу!)   -  person גלעד ברקן    schedule 10.02.2019
comment
Давайте продолжим это обсуждение в чате.   -  person taurus05    schedule 10.02.2019
comment
Пожалуйста, уточните, касается ли эта задача дерева или общего графа.   -  person גלעד ברקן    schedule 11.02.2019
comment
@גלעדברקן Речь идет о дереве, потому что, если это был граф, то предположим, что мы хотим определить отдельные пути от этого узла x к остальным узлам и, если узел x был частью цикла, то длина пути был бы бесконечен, если бы он удовлетворял всем ограничениям. Следовательно, это определенно дерево. Ни при каких обстоятельствах это не будет график.   -  person taurus05    schedule 11.02.2019
comment
Технически не все графы содержат циклы. Но даже с ориентированным ациклическим графом, я думаю, мой ответ должен работать.   -  person גלעד ברקן    schedule 12.02.2019
comment
@גלעדברקן, структура данных, связанная с этой проблемой, не является графом (ни циклическим, ни ациклическим). И что касается вашего решения, структура данных, которую вы использовали, действительно сложна, а код вообще не читается из-за использования нерелевантных имен переменных, таких как f и a, b& c.   -  person taurus05    schedule 12.02.2019
comment
Не только переменные, которые я использовал, актуальны, но и ответ выполняется со сложностью O (n). Не стесняйтесь спрашивать, если что-то кажется запутанным.   -  person גלעד ברקן    schedule 12.02.2019


Ответы (3)


Эту проблему можно решить с помощью динамического программирования на деревьях, вы можете прочитать об этом здесь https://www.geeksforgeeks.org/dynamic-programming-trees-set-2/.

Разобьем задачу на две части, первая — найти количество допустимых путей в поддереве узла u. Вторая часть — найти ответ для узла u, если мы не рассматриваем его поддерево, а идем к его родителю и так далее.

Возьмем 1 за корень дерева.

Теперь для решения первой части мы создадим массив in[], в котором мы будем хранить количество путей в поддереве узла u, поэтому in[u] будет представлять количество допустимых путей, начинающихся с узла u и посещающих его поддерево. Чтобы вычислить этот массив, мы можем запустить простой поиск в глубину следующим образом:

//u is the current node - p is the parent
void dfs1(int u, int p) {
    for (int i = 0; i < g[u].size(); i++) { //iterate through the adjacency of node u
        int v = g[u][i]; // v is the child of u
        if (v != p) { //in case v is the parent just ignore it
            dfs1(v, u); //go to node v
            if (u >= k && v >= k) { //if value of u or v is less than k then we cant start at this node so it should be 0
                in[u] += in[v] + 1; //add to number of paths of u the number of paths starting from the node v + 1 (+1 because we can also go from u to v)
            }
        }
    }
}

Для решения второй части нам нужен массив out[], где out[u] — количество допустимых путей, если мы не рассматриваем поддерево u, которое ведет к родителю u и так далее.

давайте позвоним родителю u P[u]. Чтобы вычислить out[u], мы будем полагаться на p[u]. out[i] — это число допустимых путей, если мы перейдем к p[u], и что мы можем сделать, когда доберемся до p[u], так это либо пройти через его поддерево (конечно, исключая ветвь u), либо посетить p[p[u]] (родитель родителя u), поэтому out[u] равно (out[p[u]] + 1) + (in[p[u]] - in[u] - 1).

// u is the current node - p is the parent
void dfs2(int u, int p) {
    for (int i = 0; i < g[u].size(); i++) { // iterate through the adjacency of node u
        int v = g[u][i]; // v is the child of u
        if (v != p) { // in case v is the parent just ignore it
            if (v >= k && u >= k) { // if value of u or v is less than k then we cant start at this node so it should be 0
                out[v] += (out[u] + 1); //add to the number of paths of v the number of paths from it's parent (u) + 1 if we dont use the subtree of u
                out[v] += (in[u] - in[v] - 1); // add to the number of paths of v the number of paths from it's parent (u)
                // we should subtract in[v] + 1 because we want to exclude this branch from the subtree of the parent
            }
            dfs2(v, u); // go to node v
        }
    }
}

Чтобы найти ответ, просто просуммируйте все out[u] + in[u] для всех узлов и разделите на 2, потому что каждый путь вычислялся дважды.

сложность O(V+E)

person Walid    schedule 10.02.2019

Давайте решим эту задачу в более простом случае, предположим, что все узлы в дереве больше, чем k, поэтому номер допустимого пути равен nC2.

И мы также делаем наблюдение, что допустимый путь не может содержать ни одного узла, который меньше k, поэтому нам нужно будет удалить все узлы, которые меньше k из дерева, что создаст n - k поддерево, таким образом, окончательный результат будет

result = sum of nC2 of all subtree

Простой алгоритм:

remove all edges that connect to nodes that less than k

for each node that >= k and not marked as visited
  do bfs to count number of node in that subtree
  result += nC2

return result
person Pham Trung    schedule 10.02.2019
comment
Я думаю, это самое подходящее решение на данный момент. Я жду дополнительных обзоров экспертов по этому подходу, потому что я не могу решить, лучший ли это подход или все еще существует несколько лучший подход. - person taurus05; 10.02.2019
comment
предположим, что все узлы в дереве больше, чем k, поэтому количество допустимых путей равно nC2 — а как насчет ввода k=1, 1 -> 2, 2 -> 3, 1 -> 4, 4 -> 5 5, выберите 2 = 10, но у нас есть только 3 + 3 различных допустимых пути. Я неправильно понял ваш ответ? (Кстати, я добавил рекурсивный ответ.) - person גלעד ברקן; 12.02.2019
comment
@גלעדברקן Хм, думаю, есть 10 путей: 1 -> 2, 1 -> 3, 1 -> 4, 1 -> 5, 2->3, 2->4, 2->5, 3->4, 3->5, 4->5. Я что-то упускаю? - person Pham Trung; 12.02.2019
comment
Да, вы что-то упускаете. В примере в моем комментарии выше 5 или 4 недоступны из 2. - person גלעד ברקן; 12.02.2019
comment
@גלעדברקן в таком случае у вас отсутствует требование, чтобы это было дерево :), если это ориентированный граф, то может вы и правы? Но я думаю, что это не направленно, так как нет понятия направленного дерева, а постановка задачи an edge between node u and v, а не an edge from u to v, здесь не подразумевается направление. - person Pham Trung; 12.02.2019
comment
В моем примере 2 и 4 — дети 1. Это дерево. Почему правое поддерево должно быть доступно слева, как вы, кажется, предполагаете? Может быть, я ошибался, полагая, что пути не могут вести из одного поддерева в другое? - person גלעד ברקן; 12.02.2019
comment
Вот цитата из первого комментария OP под вопросом: .. хотите подсказку, чтобы определить способ, чтобы получить все возможные пути от корневого узла к дочернему узлу. (OP означает корень для любого поддерева.) - person גלעד ברקן; 12.02.2019
comment
Вопрос не в том, существуют ли направленные деревья, а в том, предполагаются ли пути направленными сверху вниз. ОП (и я), похоже, так думали, но, возможно, ОП ошибается. - person גלעד ברקן; 12.02.2019
comment
Слово между не подразумевает направление, как вы указали, но выбор порядка для любой пары узлов на входе может подразумевать направление. - person גלעד ברקן; 12.02.2019
comment
@גלעדברקן Хм, вы правы. Если это так, и если это действительное дерево, я думаю, что алгоритм нужно будет немного изменить :) - person Pham Trung; 12.02.2019
comment
Это хорошо. Все возможности скрыты между ответами :) - person גלעד ברקן; 12.02.2019

Для деревьев, предполагая, что перечисляемые нами пути направлены сверху вниз, мы можем сформулировать это рекурсивно. Пусть f(T, k) представляет собой кортеж [a, b], где a — количество различных допустимых путей в T, начинающихся с T; и b, количество различных допустимых путей в T, которые начинаются в нижнем узле. Все узлы в допустимых путях имеют значение больше или равно k.

Затем (код Python):

def f(T, k):
  if not T["children"]:
    return [0, 0]

  result = [0, 0]

  for c in T["children"]:
    [a, b] = f(c, k)
    result[1] += a + b

    if T["value"] >= k <= c["value"]:
      # One valid path from T to c
      # plus extending all the paths
      # that start at c
      result[0] += 1 + a

  return result

Тогда ответ будет a + b после вызова f из корня дерева.

Выход:

T = {
  "value": 1,
  "children": [
    {
      "value": 2,
      "children": [
        {
          "value": 3,
          "children": [
            {
              "value": 4,
              "children": []
            }
          ]
        }
      ]
    }
  ]
}

print f(T, 2)  # [0, 3]
person גלעד ברקן    schedule 11.02.2019