Динамика по поддеревьям — различия между версиями

Материал из Викиконспекты
Перейти к: навигация, поиск
(Динамика по поддеревьям)
(Псевдокод)
Строка 34: Строка 34:
  
 
===Псевдокод===
 
===Псевдокод===
     function calculate(v, root):
+
     calculate(vertex, use_root):
         if dp[v][root] != -1:
+
         if dp[vertex][root] != -1:
             return dp[v][root]
+
             return dp[vertex][use_root]     //вернули уже посчитанное значение dp[vertex][root]
            #вернули уже посчитанное значение dp[v][root]
+
         sum = 0
         sum1 = 0
+
         if use_root == 0:                    //случай 1: не берем ребра из корня
         #случай 1: не берем ребра из корня
+
             for u in child(vertex):
        if root==0:
+
                 sum += calculate(u, 1)
             for u in child(v):
+
             dp[vertex][use_root] = sum      //выполняем мемоизацию
                 sum1 += calculate(u, 1)
+
             return sum
            #выполняем мемоизацию
+
         max1 = dp[vertex][0]
             dp[v][root] = sum1
+
         for x in child(vertex):              //случай 2: берем какое-то ребро
             return sum1
+
             max1 = max(max1, calculate(x, 0) + calculate(vertex, 0) - calculate(x, 1) + w[vertex,x])
         max1 = dp[v][0]
+
         dp[vertex][use_root] = max1         //выполняем мемоизацию
         #случай 2: берем какое-то ребро
+
         return dp[vertex][use_root]
        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]
 
  
 
==Ссылки==
 
==Ссылки==

Версия 23:49, 13 января 2013

Динамика по поддеревьям

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

Задача о максимальном взвешенном паросочетании на дереве

Формулировка

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

Решение

Максимальное взвешенное паросочетание

Главное отличие этой задачи от других динамически решаемых — ответ в одном поддереве влияет на решение в остальных.

Рассмотрим наше первое состояние, когда еще не выбрана ни одна вершина. В этом случае мы можем сделать две вещи:

  • Разрешить выбирать ребро из корня к ребенку.
  • Запретить выбирать ребра из корня.

Если мы запрещаем, значит можем разрешить всем его детям выбрать ребро из своего корня к своим детям. В ином случае мы можем разрешить не всем детям, а только тем, которые не были выбраны ребром из корня.

Рекуррентная формула

Обозначим в качестве [math]dp(vertex, use\_root)[/math] функцию, возвращающую ответ для поддерева с корнем [math]vertex[/math]. Если [math]use\_root=1[/math], то в этом поддереве мы разрешаем занимать корень, иначе нет. Обозначим вес ребра из [math]v[/math] в [math]u[/math] как [math]w[v,u][/math]

[math]dp(u, 0) = \sum_{\text{child}\ v\ of\ u}dp(v, 1)[/math]
[math]dp(u, 1) = \max\left\{dp(u, 0),\ \max_{\text{child}\ x\ of\ u}\{dp(x, 0)\ +\ \sum_{\text{child}\ v\ of\ u; \ v \ne x }dp(v, 1)\ +\ w[u, x] \}\right\}[/math]

Заметим, что вторую формулу можно упростить:
[math]\sum_{\text{child}\ v\ of\ u; \ v \ne x }dp(v, 1) = dp(u, 0) - dp(x, 1)[/math]

Теперь наши формулы имеют вид:
[math]dp(u, 0) = \sum_{\text{child}\ v\ of\ u}dp(v, 1)[/math]
[math]dp(u, 1) = \max\left\{dp(u, 0),\ \max_{\text{child}\ x\ of\ u}\{dp(x, 0)\ +\ dp(u, 0) - dp(x, 1)\ +\ w[u,x] \}\right\}[/math]

Заметим, что с помощью этого преобразования мы сократили общее время вычисления с [math]O(n^2)[/math] до [math]O(n)[/math].

Псевдокод

   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]

Ссылки