Динамика по поддеревьям — различия между версиями
Mihver1 (обсуждение | вклад) (→Динамика по деревьям) |
Mihver1 (обсуждение | вклад) м (→Решение) |
||
Строка 16: | Строка 16: | ||
В первом случае мы не сможем рассматривать его детей вовсе. В ином случае мы переходим в его поддеревья и выполняем то же самое действие. | В первом случае мы не сможем рассматривать его детей вовсе. В ином случае мы переходим в его поддеревья и выполняем то же самое действие. | ||
− | Если мы оставляем корень свободным, значит | + | Если мы оставляем корень свободным, значит можем разрешить всем его детям иметь в своих поддеревьях занятый корень. В ином случае мы можем разрешить не всем детям, а только тем, которые уже не заняты ребром из корня. |
===Рекуррентная формула=== | ===Рекуррентная формула=== |
Версия 21:28, 13 января 2013
Содержание
Динамика по деревьям
Главной особенностью динамического программирования по дереву является необходимость учитывать ответы в поддеревьях, т.к. они могут влиять на другие поддеревья. Рассмотрим для лучшего понимания динамики по поддеревьям задачу о максимальном взвешенном паросочетании в дереве.
Задача о максимальном взвешенном паросочетании на дереве
Формулировка
Пусть дано подвешенное за корень дерево, имеющее веса на каждом из ее ребер. Необходимо выбрать такое множество ребер, что бы сумма значений была максимальной и при этом выбранные ребра не являлись бы соседними. Т.е. необходимо решить задачу о максимальном взвешенном паросочетании.
Решение
Главное отличие этой задачи от других динамически решаемых — ответ в одном поддереве влияет на решение в остальных.
Рассмотрим наше первое состояние, когда еще не выбрана ни одна вершина. В этом случае мы можем сделать две вещи:
- Занять корень каким-то ребром
- Не занимать корень ребрами
В первом случае мы не сможем рассматривать его детей вовсе. В ином случае мы переходим в его поддеревья и выполняем то же самое действие. Если мы оставляем корень свободным, значит можем разрешить всем его детям иметь в своих поддеревьях занятый корень. В ином случае мы можем разрешить не всем детям, а только тем, которые уже не заняты ребром из корня.
Рекуррентная формула
Обозначим в качестве
функцию, возвращающую ответ для поддерева с корнем и использующего корень, если и наоборот.
Заметим, что вторую формулу можно упростить:
Теперь наши формулы имеют вид:
Заметим, что с помощью этого преобразования мы сократили общее время вычисления с
до .Псевдокод
function calculate(v, root): if dp[v][root] != -1: return dp[v][root] #вернули уже посчитанное значение dp[v][root] sum1 = 0 #случай 1: не берем ребра из корня if root==0: for u in child(v): sum1 += calculate(u, 1) #выполняем мемоизацию dp[v][root] = sum1 return sum1 max1 = dp[v][0] #случай 2: берем какое-то ребро for x in child(v): max1 = max(max1, calculate(x, 0) + calculate(v, 0) - calculate(x, 1) + w[v,x]) # выполняем мемоизацию dp[v][root] = max1 return dp[v][root]