Динамика по поддеревьям — различия между версиями
Sketcher (обсуждение | вклад) (→Задача о паросочетании максимального веса в дереве) |
Sketcher (обсуждение | вклад) |
||
Строка 3: | Строка 3: | ||
== Задача о паросочетании максимального веса в дереве == | == Задача о паросочетании максимального веса в дереве == | ||
+ | Пусть задано взвешенное дерево, с весами, обозначенными как <tex>w_{i,j}</tex>, где <tex>i</tex> и <tex>j</tex> — вершины дерева, соединённые ребром. | ||
{{Задача | {{Задача | ||
|definition = Cоставить такое [[Теорема_о_максимальном_паросочетании_и_дополняющих_цепях | паросочетание]], чтобы суммарный вес всех рёбер, входящих в него, был максимальным. | |definition = Cоставить такое [[Теорема_о_максимальном_паросочетании_и_дополняющих_цепях | паросочетание]], чтобы суммарный вес всех рёбер, входящих в него, был максимальным. | ||
}} | }} | ||
− | |||
− | |||
Для решения данной задачи существует несколько алгоритмов. Например, [[алгоритм_Куна_для_поиска_максимального_паросочетания | алгоритм Куна]], который имеет верхнюю оценку порядка <tex>O \left ( n^3 \right )</tex>. Но так как нам дано дерево, то можно использовать динамическое программирование, время работы алгоритма с которым улучшается до <tex>O \left ( n \right )</tex>. | Для решения данной задачи существует несколько алгоритмов. Например, [[алгоритм_Куна_для_поиска_максимального_паросочетания | алгоритм Куна]], который имеет верхнюю оценку порядка <tex>O \left ( n^3 \right )</tex>. Но так как нам дано дерево, то можно использовать динамическое программирование, время работы алгоритма с которым улучшается до <tex>O \left ( n \right )</tex>. | ||
Строка 28: | Строка 27: | ||
Теперь оценим количество операций, необходимых нам для нахождения <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>, где <tex> n </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>, где <tex> n </tex> — количество вершин в дереве. | ||
+ | |||
=== Псевдокод === | === Псевдокод === | ||
Строка 40: | Строка 40: | ||
== Задача о сумме длин всех путей в дереве == | == Задача о сумме длин всех путей в дереве == | ||
− | + | {{Задача | |
− | Решим эту задачу за <tex> O(n) </tex>. | + | |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> количество путей оканчивающихся вершиной <tex> v </tex>, <tex> H[v]\ - </tex> количество путей проходящих через вершину <tex> v </tex>. Если вершина <tex> u </tex> лист, то <tex> S[u] </tex> = 1, а <tex> G[u] </tex> = 0. | Теперь подсчитаем пути для каждого варианта. Обозначим <tex> S[v]\ - </tex> размер поддерева <tex> v </tex>, <tex> F[v]\ - </tex> сумма длин всех путей вершины <tex> v </tex>, <tex> G[v]\ - </tex> количество путей оканчивающихся вершиной <tex> v </tex>, <tex> H[v]\ - </tex> количество путей проходящих через вершину <tex> v </tex>. Если вершина <tex> u </tex> лист, то <tex> S[u] </tex> = 1, а <tex> G[u] </tex> = 0. | ||
− | + | # Пути не проходящие через эту вершину. Это просто сумма суммы длин для всех поддеревьев детей или <tex> \sum_{x \in Ch(v)} \limits F[x]</tex>. | |
− | + | # Пути оканчивающиеся в вершине <tex> v </tex>. Рассмотрим ребро, соединяющее вершину <tex> v </tex> и одного ее сына, пусть это будет вершина <tex> g </tex>. Переберем все пути, которые начинаются с этого ребра и идут вниз. Это будет сумма путей оканчивающихся в <tex> g + S[g] </tex>, так как суммарная длина поддерева <tex> g </tex> уже сосчитана и каждый такой путь мы продлили ребром, соединяющим вершины <tex> v </tex> и <tex> g </tex>. Всего таких путей: <tex> G[v] = \sum_{x \in Ch(v)} \limits {(G[x] + S[x])}</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{(G[x]S[y] + G[y]S[x] + 2S[x]S[y])} </tex>. Но такой подсчет испортит асимптотику до <tex> O(n^2) </tex>. Заметим, что <tex> \sum_{x,y} \limits {(G[x]S[y])} = \sum_{x} \limits {G[x]} \sum_{y} \limits {S[y]} </tex>. Но еще надо учесть, что <tex> x \ne y </tex>, следовательно <tex> \sum_{x,y\ x \ne y} \limits{(G[x]S[y])} = \sum_{x} \limits {G[x]} \sum_{y} \limits {S[y]} - \sum_{x} \limits {(G[x]S[x])} </tex>. Аналогично для <tex> S[x]S[y] </tex>. Итак: <tex> H[v] = 2(\sum_{x} \limits {G[x]} \sum_{y} \limits {S[y]} - \sum_{x} \limits {(G[x]S[x])} ) + | |
− | |||
− | |||
− | |||
− | |||
− | Заметим, что <tex> \sum_{x,y} \limits {(G[x]S[y])} = \sum_{x} \limits {G[x]} \sum_{y} \limits {S[y]} </tex>. Но еще надо учесть, что <tex> x \ne y </tex>, следовательно <tex> \sum_{x,y\ x \ne y} \limits{(G[x]S[y])} = \sum_{x} \limits {G[x]} \sum_{y} \limits {S[y]} - \sum_{x} \limits {(G[x]S[x])} </tex>. Аналогично для <tex> S[x]S[y] </tex>. Итак: <tex> H[v] = 2(\sum_{x} \limits {G[x]} \sum_{y} \limits {S[y]} - \sum_{x} \limits {(G[x]S[x])} ) + | ||
2(\sum_{x} \limits {S[x]} \sum_{y} \limits {S[y]} - \sum_{x} \limits {(S[x]S[x])})</tex>. | 2(\sum_{x} \limits {S[x]} \sum_{y} \limits {S[y]} - \sum_{x} \limits {(S[x]S[x])})</tex>. | ||
Версия 20:44, 6 января 2017
Главной особенностью динамического программирования по поддеревьям является необходимость учитывать ответы в поддеревьях, так как они могут влиять на ответы в других поддеревьях. Рассмотрим для лучшего понимания динамики по поддеревьям задачу о максимальном взвешенном паросочетании в дереве.
Содержание
Задача о паросочетании максимального веса в дереве
Пусть задано взвешенное дерево, с весами, обозначенными как
, где и — вершины дерева, соединённые ребром.Задача: |
Cоставить такое паросочетание, чтобы суммарный вес всех рёбер, входящих в него, был максимальным. |
Для решения данной задачи существует несколько алгоритмов. Например, алгоритм Куна, который имеет верхнюю оценку порядка . Но так как нам дано дерево, то можно использовать динамическое программирование, время работы алгоритма с которым улучшается до .
Обозначим
как паросочетание максимального веса в поддереве с корнем в -той вершине, при этом -тая вершина соединена ребром, входящим в паросочетание, с вершиной, входящей в поддерево -ой вершины; аналогично — как паросочетание максимального веса в поддерева с корнем в -той вершине, но только при этом -тая вершина соединена ребром, входящим в паросочетание, с вершиной, не входящей в поддерево -ой вершины; а , таким образом, ответ на задачу будет находиться в , где — корень дерева. Идея динамического программирования здесь состоит в том, что для того, чтобы найти паросочетание максимального веса с корнем в вершине , нам необходимо найти максимальное паросочетание для всех поддеревьев -ой вершины.Обозначим
— как множество сыновей вершины и будем находить значения и следующим образом:Если вершина
— лист, то ,в противном же случае
- ,
С учётом того, что
, эти формулы можно переписать как- .
Теперь оценим количество операций, необходимых нам для нахождения . Так как , то для вычисления необходимо вычислить , . Для вычисления и того, и другого необходимо время порядка , где — количество вершин в дереве.
Псевдокод
function dfs(x: int): //в основной процедуре вызываем dfs от корня(root), после этого ответ будет хранится в c[root] for (i : Ch[x]) dfs(i) a[x] = max(a[x], b[i] + w[x][i] - с[i]) // по формуле выше, но без b[x] (прибавим его один раз в конце) b[x] += с[i] a[x] += b[x] // так как в a[x] пока что хранится только на сколько мы можем увеличить ответ если будем использовать вершину x c[x] = max(a[x], b[x])
Задача о сумме длин всех путей в дереве
Задача: |
Найти сумму длин всех путей в дереве. |
Решим эту задачу за
. Пусть задано подвешенное дерево. Рассмотрим количество путей для вершины . Во-первых, это пути не проходящие через эту вершину, то есть все пути между её сыновьями. Во-вторых, пути, которые оканчиваются вершиной . И в третьих, это пути проходящие через вершину , они начинаются из поддерева одного из сыновей этой вершины и заканчиваются в другом поддереве одного из сыновей вершины .Теперь подсчитаем пути для каждого варианта. Обозначим
размер поддерева , сумма длин всех путей вершины , количество путей оканчивающихся вершиной , количество путей проходящих через вершину . Если вершина лист, то = 1, а = 0.- Пути не проходящие через эту вершину. Это просто сумма суммы длин для всех поддеревьев детей или .
- Пути оканчивающиеся в вершине . Рассмотрим ребро, соединяющее вершину и одного ее сына, пусть это будет вершина . Переберем все пути, которые начинаются с этого ребра и идут вниз. Это будет сумма путей оканчивающихся в , так как суммарная длина поддерева уже сосчитана и каждый такой путь мы продлили ребром, соединяющим вершины и . Всего таких путей: .
- Пути проходящие через вершину . Рассмотрим двух сыновей этой вершины: и . Нам надо подсчитать все пути, которые поднимаются из поддерева в и затем опускаются в поддерево и наоборот. То есть по каждому пути, оканчивающимся в вершине мы пройдем столько раз сколько элементов в поддереве , следовательно таких путей будет . Аналогично, если будем подниматься из поддерева . Также надо учитывать сколько раз мы проходим по ребрам, соединяющим вершины и . Итого для двух вершин и : , следовательно ( ) . Но такой подсчет испортит асимптотику до . Заметим, что . Но еще надо учесть, что , следовательно . Аналогично для . Итак: .
Ответ задачи:
. Асимптотика каждого слагаемого равна , где — количество вершин в дереве, следовательно и время работы самого алгоритма .Амортизированные оценки для ДП на дереве
Теорема: |
Пусть какой-либо алгоритм на дереве работает за время для вершины x, тогда время обработки им всего дерева не превышает : |
Доказательство: |
, поэтому . |