Динамика по поддеревьям — различия между версиями
Mihver1 (обсуждение | вклад) м (→Формулировка)  | 
				Mihver1 (обсуждение | вклад)  м (→Рекуррентная формула)  | 
				||
| Строка 18: | Строка 18: | ||
===Рекуррентная формула===  | ===Рекуррентная формула===  | ||
| − | Обозначим в качестве <tex>dp(vertex, use\_root)</tex> функцию, возвращающую ответ для поддерева с корнем <tex>  | + | Обозначим в качестве <tex>dp(vertex, use\_root)</tex> функцию, возвращающую ответ для поддерева с корнем <tex>vertex</tex>.  | 
Если <tex>use\_root=1</tex>, то в этом поддереве мы разрешаем занимать корень, иначе нет. Обозначим вес ребра из <tex>v</tex> в <tex>u</tex> как <tex>w[v,u]</tex>    | Если <tex>use\_root=1</tex>, то в этом поддереве мы разрешаем занимать корень, иначе нет. Обозначим вес ребра из <tex>v</tex> в <tex>u</tex> как <tex>w[v,u]</tex>    | ||
Версия 23:43, 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]
