Количество различных путей в дереве, у которых значение узлов в этом пути больше или равно K
Постановка задачи:
Вам дано целое число N
обозначая количество узлов в этом дереве. Теперь вам нужно посчитать, сколько разных путей есть в дереве, так что минимальное значение узла в этом пути больше или равно k
,
Формат ввода:
- Первая строка содержит общее количество узлов
N
в этом дереве и положительное целое значениеK
, - следующий
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).
3 ответа
Эта проблема может быть решена с помощью динамического программирования на деревьях, вы можете прочитать об этом здесь https://www.geeksforgeeks.org/dynamic-programming-trees-set-2/.
Давайте разделим задачу на две части. Первая - найти количество путей, которые являются действительными в поддереве узла. u
, Вторая часть - найти ответ для узла. u
если мы не рассмотрим его поддерево, а перейдем к его родителю и так далее.
Рассмотрим 1 как корень дерева.
Теперь для решения первой части сделаем массив in[]
в котором мы будем хранить количество путей в поддереве узла u
так in[u]
будет представлять количество действительных путей, начиная с узла u
и посещение его поддерева. Чтобы вычислить этот массив, мы можем запустить простой DFS следующим образом:
//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)
Давайте решим эту проблему в более простом случае, предположим, что все узлы в дереве больше, чем 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
Для деревьев, предполагая, что пути, которые мы перечисляем, направлены сверху вниз, мы можем сформулировать это рекурсивно. Позволять 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]