Динамика по поддеревьям — различия между версиями
Дмитрий (обсуждение | вклад) м |
Дмитрий (обсуждение | вклад) (→Задача о максимальном взвешенном паросочетании на дереве) |
||
Строка 3: | Строка 3: | ||
Главной особенностью [[динамическое программирование|динамического программирования]] по поддеревьям является необходимость учитывать ответы в поддеревьях, так как они могут влиять на ответы в других поддеревьях. | Главной особенностью [[динамическое программирование|динамического программирования]] по поддеревьям является необходимость учитывать ответы в поддеревьях, так как они могут влиять на ответы в других поддеревьях. | ||
Рассмотрим для лучшего понимания динамики по поддеревьям задачу о максимальном взвешенном паросочетании в дереве. | Рассмотрим для лучшего понимания динамики по поддеревьям задачу о максимальном взвешенном паросочетании в дереве. | ||
− | ==Задача о | + | == Задача о паросочетании максимального веса в дереве == |
− | |||
− | |||
− | + | {{Определение | definition = | |
− | [[ | + | Множество рёбер графа, такое, что у любых двух рёбер нет общей вершины, называется [[Теорема_о_максимальном_паросочетании_и_дополняющих_цепях | паросочетанием]]. |
− | + | }} | |
− | |||
− | |||
− | |||
− | + | Пусть задано взвешенное дерево, с весами, обозначенными как <tex>w_{i,j}</tex>, где <tex>i</tex> и <tex>j</tex> — вершины дерева, соединённые ребром. Задача: составить такое паросочетание, чтобы суммарный вес всех рёбер, входящих в него, был максимальным. | |
− | + | Для решения данной задачи существует несколько алгоритмов. Например, [[алгоритм_Куна_для_поиска_максимального_паросочетания | алгоритм Куна]], который имеет верхнюю оценку порядка <tex>O \left ( n^3 \right )</tex>. Но так как нам дано дерево, то можно использовать динамическое программирование, время работы алгоритма с которым улучшается до <tex>O \left ( n \right )</tex>. | |
− | |||
− | |||
− | <tex> | + | Обозначим <tex>a[i]</tex> как паросочетание максимального веса в поддереве с корнем в i-той вершине, при этом i-тая вершина соединена ребром, входящим в паросочетание, с вершиной, входящей в поддерево i-ой вершины; аналогично <tex>b[i]</tex> - как паросочетание максимального веса в поддерева с корнем в i-той вершине, но только при этом i-тая вершина соединена ребром, входящим в паросочетание, с вершиной, не входящей в поддерево i-ой вершины; а <tex>c[i]=\max \left ( a[i],b[i] \right )</tex>, таким образом, ответ на задачу будет находиться в <tex>c[root]</tex>, где <tex>root</tex> - корень дерева. Идея динамического программирования здесь состоит в том, что для того, чтобы найти паросочетание максимального веса с корнем в вершине i, нам необходимо найти максимальное паросочетание для всех поддеревьев i-ой вершины. |
− | <tex> | ||
− | + | Обозначим Ch(x) — как множество сыновей вершины x и будем находить значения ''a'' и ''b'' следующим образом: | |
− | |||
− | + | Если вершина x — лист, то <tex>a[x]=b[x]=0</tex>, | |
− | |||
− | |||
− | + | в противном же случае | |
+ | |||
+ | * <tex>a[x]=\max_{y \in Ch(x)}\limits \left ( b[y]+w_{x,y} +\sum_{\substack{z \neq y\\z \in Ch(x)}} \limits \max \left ( a[z],b[z] \right )\right )</tex>, | ||
+ | * <tex>b[x]=\sum_{z \in Ch(x)} \limits \max \left ( a[z], b[z] \right )</tex> | ||
+ | |||
+ | С учётом того, что <tex>c[i]=\max \left ( a[i],b[i] \right )</tex>, эти формулы можно переписать как | ||
+ | |||
+ | * <tex>a[x]=\max_{y \in Ch(x)}\limits \left ( b[y]+w_{x,y}-c[y] \right )+b[x]</tex> | ||
+ | * <tex>b[x]=\sum_{z \in Ch(x)} \limits c[z]</tex>. | ||
+ | |||
+ | |||
+ | Теперь оценим количество операций, необходимых нам для нахождения <tex>c[root]</tex>. Так как <tex>c[i]=\max \left ( a[i],b[i] \right )</tex>, то для вычисления <tex>c[root]</tex> необходимо вычислить <tex>a[root]</tex>, <tex>b[root]</tex>. Для вычисления и того, и другого необходимо время порядка <tex>O \left ( \sum_{x=1}^n \limits \left | Ch(x) \right | \right )=O \left ( n \right )</tex>, где n — количество вершин в дереве. | ||
===Реализация=== | ===Реализация=== |
Версия 22:43, 1 января 2014
Содержание
Динамика по поддеревьям
Главной особенностью динамического программирования по поддеревьям является необходимость учитывать ответы в поддеревьях, так как они могут влиять на ответы в других поддеревьях. Рассмотрим для лучшего понимания динамики по поддеревьям задачу о максимальном взвешенном паросочетании в дереве.
Задача о паросочетании максимального веса в дереве
Определение: |
Множество рёбер графа, такое, что у любых двух рёбер нет общей вершины, называется паросочетанием. |
Пусть задано взвешенное дерево, с весами, обозначенными как
, где и — вершины дерева, соединённые ребром. Задача: составить такое паросочетание, чтобы суммарный вес всех рёбер, входящих в него, был максимальным.Для решения данной задачи существует несколько алгоритмов. Например, алгоритм Куна, который имеет верхнюю оценку порядка . Но так как нам дано дерево, то можно использовать динамическое программирование, время работы алгоритма с которым улучшается до .
Обозначим
как паросочетание максимального веса в поддереве с корнем в i-той вершине, при этом i-тая вершина соединена ребром, входящим в паросочетание, с вершиной, входящей в поддерево i-ой вершины; аналогично - как паросочетание максимального веса в поддерева с корнем в i-той вершине, но только при этом i-тая вершина соединена ребром, входящим в паросочетание, с вершиной, не входящей в поддерево i-ой вершины; а , таким образом, ответ на задачу будет находиться в , где - корень дерева. Идея динамического программирования здесь состоит в том, что для того, чтобы найти паросочетание максимального веса с корнем в вершине i, нам необходимо найти максимальное паросочетание для всех поддеревьев i-ой вершины.Обозначим Ch(x) — как множество сыновей вершины x и будем находить значения a и b следующим образом:
Если вершина x — лист, то
,в противном же случае
- ,
С учётом того, что
, эти формулы можно переписать как- .
Теперь оценим количество операций, необходимых нам для нахождения . Так как , то для вычисления необходимо вычислить , . Для вычисления и того, и другого необходимо время порядка , где n — количество вершин в дереве.
Реализация
Заполним изначально массив
числами -1. ( — число вершин)calculate(v, useRoot): if dp[v][useRoot] != -1: return dp[v][useRoot] //вернули уже посчитанное значение dp[vertex][root] sum = 0 if useRoot == 0: //случай 1: не берем ребра из корня for (для) всех u из множества детей v: sum += calculate(u, 1) dp[v][useRoot] = sum else: //случай 2: берем какое-то ребро max1 = dp[v][0] for (для) всех x из множества детей v: max1 = max(max1, calculate(x, 0) + calculate(vertex, 0) - calculate(x, 1) + w[v,x]) dp[v][useRoot] = max1 return dp[v][useRoot]