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