1632
правки
Изменения
м
TODO:* Merge со статьей berkeley* Добавить еще примерыГлавной особенностью [[динамическое программирование|динамического программирования]] по [[Дерево, эквивалентные определения | поддеревьям]] является необходимость учитывать ответы в поддеревьях, так как они могут влиять на ответы в других поддеревьях.* Конспект с лекции. Добавить затронутые темыРассмотрим для лучшего понимания динамики по поддеревьям задачу о максимальном взвешенном паросочетании в дереве.
Дерево {{---}} очередная структура данных, на которой можно увидеть принцип разбиения решения на подзадачи. Представьте дерево <tex>T</tex> с <tex>n</tex> вершинами. Так как в поддеревья <tex>T</tex> входят все потомки корня, то поддеревьев в <tex>T</tex> ровно столько же, сколько и вершин (<tex>n</tex> в данном случае).== Максимальное независимое множество Задача о паросочетании максимального веса в дереве ==
Если данный граф является деревом{{Задача|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>Ch(x)</tex> {{---}} как множество сыновей вершины <tex>x</tex> и будем находить значения <tex>a[x]</tex> и назовем её <tex>rb[x]</tex> следующим образом: Если вершина <tex>x</tex> {{---}} лист, то <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>Ic[i]=\max \left (ua[i],b[i] \right )</tex> обозначает размер максимального независимого множества вершин поддерева, корнем которого является вершина то для вычисления <tex>c[root]</tex> необходимо вычислить <tex>a[root]</tex>, <tex>ub[root]</tex>. Тогда ответом на задачу будет являться Для вычисления и того, и другого необходимо время порядка <tex>IO \left ( \sum_{x=1}^n \limits \left | Ch(x) \right | \right )=O \left (rn \right )</tex>, где <tex> n </tex> — число вершин в дереве.
Нетрудно видеть, что если мы включаем вершину ''u'' в максимальное независимое множество, то его [[Мощность множества|мощность]] увеличивается на 1, но его детей мы брать не можем (так как они соединены ребром с вершиной ''u''); если же мы не включаем эту вершину, то мощность максимального независимого множества будет равна сумме размеров независимых множеств детей этой вершины. Остается только выбрать максимум из этих двух вариантов, чтобы получить решение задачи:
: <tex>I(u) = \max\left\{1\ +\ \sum_{\text{grandchild}\ w\ of\ u}I(w),\ \sum_{\text{child}\ w\ of\ u}I(w) \right\},</tex>
где grandchild обозначает всякого «внука» вершины, а child обозначает всякого потомка вершины.
Считаем <font color = darkgreen>// в основной процедуре вызываем dfs от корня(root), после этого ответ будет хранится в c[root] </font color = darkgreen> '''function''' dfs(x: '''int''', a: '''int[]''', b: '''int[]''', c: '''int[]''', w: '''int[][]''', Ch: '''int[]'''): '''for''' (i : Ch[x]) dfs(i, a, b, c, w, Ch) a[x] = max(a[x], b[i] + w[x][i] - с[i]) <font color = darkgreen>// по формуле выше, но без b[x] (прибавим его один раз в конце) </font color = darkgreen> b[x] += с[i] a[x] += b[x] <font color = darkgreen>// так как в a[x] пока что хранится только на сколько мы можем увеличить ответ если будем использовать вершину x</font color = darkgreen> c[x] = max(a[x], b[x]) == Задача о сумме длин всех путей в дереве =={{Задача|definition = Найти сумму длин всех путей в дереве.}}Решим эту задачу за <tex> O(n) </tex>. Пусть задано подвешенное дерево. Рассмотрим пути проходящие в поддереве вершины <tex> v </tex>. Во-первых, это пути, не проходящие через эту вершину, то есть все пути в поддеревьях её сыновей. Во-вторых, пути, которые оканчиваются вершиной <tex> v </tex>. И в-третьих, это пути, проходящие через вершину <tex> v </tex>, они начинаются из поддерева одного из сыновей этой вершины и заканчиваются в другом поддереве одного из сыновей вершины <tex> v </tex>. Теперь подсчитаем пути для каждого варианта. Обозначим <tex> S[v]\ - </tex> размер поддерева <tex> v </tex>, <tex> F[v]\ - </tex> сумма длин всех путей в поддереве вершины <tex> v </tex>, <tex> G[v]\ - </tex> сумма длин всех путей начинающихся в поддереве вершины v и оканчивающихся вершиной <tex> v </tex>, <tex> H[v]\ - </tex> сумма длин всех путей проходящих через вершину <tex> v </tex>. Если вершина <tex> u </tex> лист, то <tex> S[u] </tex> = 1, а <tex> G[u] </tex> = <tex> H[u] </tex> = 0.# Пути не проходящие через эту вершину. Это просто сумма суммы длин путей для всех поддеревьев детей или <tex> \sum_{x \in Ch(v)} \limits F[x]</tex>.# Пути оканчивающиеся в вершине u хранится <tex>Iv </tex>. Рассмотрим ребро, соединяющее вершину <tex> v </tex> и одного ее сына, пусть это будет вершина <tex> g </tex>. Переберем все пути, которые начинаются с этого ребра и идут вниз. Сумма длин всех таких путей будет сумма путей оканчивающихся в <tex> g + S[g] </tex>, так как суммарная длина путей оканчивающихся в вершине <tex> g </tex> уже сосчитана и каждый такой путь, которых ровно <tex> S[g] </tex> мы продлили ребром, соединяющим вершины <tex> v </tex> и <tex> g </tex>. Суммарная длина таких путей: <tex> G[v] = \sum_{x \in Ch(v)} \limits {\Bigl(G[x] + S[x]\Bigl)}</tex>.# Пути проходящие через вершину <tex> v </tex>. Рассмотрим двух сыновей этой вершины: <tex> x </tex> и <tex> y </tex>. Нам надо подсчитать все пути, которые поднимаются из поддерева <tex> x </tex> в <tex> v </tex> и затем опускаются в поддерево <tex> y </tex> и наоборот. То есть по каждому пути, оканчивающимся в вершине <tex> x </tex> мы пройдем столько раз сколько элементов в поддереве <tex> y </tex>, следовательно суммарная длина таких путей будет <tex> G[x]S[y] </tex>. Аналогично, если будем подниматься из поддерева <tex> y </tex>. Также надо учитывать сколько раз мы проходим по ребрам, соединяющим вершины <tex> x </tex> <tex> v </tex> и <tex> y </tex> <tex> x </tex>. Итого для двух вершин <tex> x </tex> и <tex> y </tex>: <tex> G[x]S[y] + G[y]S[x] + 2S[x]S[y] </tex>, следовательно ( <tex> x,y \in Ch(v)</tex>) <tex> H[v] = \sum_{x,y\ x \ne y} \limits{\Bigl(G[x]S[y] + G[y]S[x] + 2S[x]S[y]\Bigl)} </tex>. Но такой подсчет испортит асимптотику до <tex> O(n^2) </tex>. Заметим, что <tex> \sum_{x,y} \limits {\Bigl(G[x]S[y]\Bigl)} = \sum_{x} \limits {G[x]} \sum_{y} \limits {S[y]} </tex>. Но еще надо учесть, что <tex> x \ne y </tex>, следовательно <tex> \sum_{x,y\ x \ne y} \limits{\Bigl(G[x]S[y]\Bigl)} = \sum_{x} \limits {G[x]} \sum_{y} \limits {S[y]} - \sum_{x} \limits {\Bigl(G[x]S[x]\Bigl)} </tex>. Аналогично для <tex> S[x]S[y] </tex>. Итак: <tex> H[v] = \biggl(\sum_{x} \limits {G[x]} \sum_{y} \limits {S[y]} - \sum_{x} \limits {\Bigl(G[x]S[x]\Bigl)} \biggl) + \biggl(\sum_{x} \limits {S[x]} \sum_{y} \limits {S[y]} - \sum_{x} \limits {\Bigl(S[x]S[x]\Bigl)} \biggl) </tex>. Ответ задачи: <tex> F[v] = \sum_{x \in Ch(v)} \limits F[x] + G[v] + H[v] </tex>. Асимптотика каждого слагаемого равна <tex>O \left ( \sum_{x=1}^n \limits \left | Ch(x) \right | \right )=O \left ( n \right )</tex>, где <tex> n </tex> — число вершин в дереве, следовательно и время работы самого алгоритма <tex> O \left (n \right ) </tex>. == Амортизированные оценки для ДП на дереве =={{Теорема|statement=Пусть какой-либо алгоритм на дереве работает за время <tex>O \left ( \left |Ch \left ( x \right) \right |^k \right )</tex> для вершины x, тогда время обработки им всего дерева не превышает <tex>O \left (un^k \right )</tex>:|proof=<tex>\forall x \in \left \{ 1 \dots n \right \}: \left | Ch(x) \right | \leqslant n</tex>, поэтому <tex>\sum_{x=1}^n \limits \left | Ch \left ( x \right ) \right |^k \leqslant \sum_{x=1}^n \limits | Ch \left ( x \right ) | \cdot n^{k-1}=n \cdot n^{k-1}=n^k</tex>.}} ==См. также==* [[Задача коммивояжера, ДП по подмножествам]]* [[Задача о числе путей в ациклическом графе]]
get_independent_set(Node u) ==Источники информации== если I(u) уже посчитано, то возвратить I(u) *[http://www.mathnet.ru/мощность множества, которое можно получить, если не брать вершину u children_sum = 0 links/c14aca73a4926918a879905ffcd4ad7a/мощность множестваtimb86.pdf В. В. Лепин, которое можно получить, если взять вершину uЛинейный алгоритм для нахождения максимального индуцированного паросочетания наименьшего веса в реберно-взвешенном дереве] grandchildren_sum = 0 * [http://цикл по всем детям for i := 1 to child_num do children_sum = children_sum + get_independent_set(children[i]) ru.wikipedia.org/wiki/цикл по всем внукам for i:= 1 to grandchildren_num grandchildren_sum = grandchildren_sum + get_independent_set(grandchildren[iПаросочетание Википедия — Паросочетание]) //запоминаем, чтобы не персчитывать ещё раз I(u) = max(1 + grandchildren_sum, children_sum) возвратить I(u)
Вызов get_independent_set(''r'') даст ответ [[Категория: Дискретная математика и алгоритмы]][[Категория: Динамическое программирование]][[Категория: Другие задачи динамического программирования]][[Категория:Алгоритмы на задачу. Время выполнения алгоритма, очевидно, <tex>O(|V| + |E|)</tex>.графах]]
rollbackEdits.php mass rollback
Обозначим <tex>a[i]</tex> как паросочетание максимального веса в поддереве с корнем в <tex>i</tex>-той вершине, при этом <tex>i</tex>-тая вершина соединена ребром, входящим в паросочетание, с вершиной, входящей в поддерево <tex>i</tex>-ой вершины; аналогично <tex>b[i]</tex> {{---}} как паросочетание максимального веса в поддерева с корнем в <tex>i</tex>-той вершине, но только при этом <tex>i</tex>-тая вершина соединена ребром, входящим в паросочетание, с вершиной, не входящей в поддерево <tex>i</tex>-ой вершины; а <tex>c[i]=== Оптимальная подструктура задачи ===\max \left ( a[i],b[i] \right )</tex>, таким образом, ответ на задачу будет находиться в <tex>c[root]</tex>, где <tex>root</tex> {{---}} корень дерева. Идея динамического программирования здесь состоит в том, что для того, чтобы найти паросочетание максимального веса с корнем в вершине <tex>i</tex>, нам необходимо найти максимальное паросочетание для всех поддеревьев <tex>i</tex>-ой вершины.
=== Псевдокод ===