Динамика по поддеревьям — различия между версиями
Shersh (обсуждение | вклад) м (→Псевдокод) |
Sketcher (обсуждение | вклад) (→Псевдокод) |
||
| Строка 31: | Строка 31: | ||
=== Псевдокод === | === Псевдокод === | ||
<font color = darkgreen>// в основной процедуре вызываем dfs от корня(root), после этого ответ будет хранится в c[root] </font color = darkgreen> | <font color = darkgreen>// в основной процедуре вызываем dfs от корня(root), после этого ответ будет хранится в c[root] </font color = darkgreen> | ||
| − | '''function''' dfs(x: '''int''', a: '''int''', b: '''int''', c: '''int'''): | + | '''function''' dfs(x: '''int''', a: '''int[]''', b: '''int[]''', c: '''int[]''', w: '''int[][], Ch: '''int[]'''): |
'''for''' (i : Ch[x]) | '''for''' (i : Ch[x]) | ||
| − | dfs(i) | + | 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> | a[x] = max(a[x], b[i] + w[x][i] - с[i]) <font color = darkgreen>// по формуле выше, но без b[x] (прибавим его один раз в конце) </font color = darkgreen> | ||
b[x] += с[i] | b[x] += с[i] | ||
Версия 17:13, 7 января 2017
Главной особенностью динамического программирования по поддеревьям является необходимость учитывать ответы в поддеревьях, так как они могут влиять на ответы в других поддеревьях. Рассмотрим для лучшего понимания динамики по поддеревьям задачу о максимальном взвешенном паросочетании в дереве.
Содержание
Задача о паросочетании максимального веса в дереве
| Задача: |
| Пусть задано взвешенное дерево, с весами, обозначенными как , где и — вершины дерева, соединённые ребром.. Необходимо составить такое паросочетание, чтобы суммарный вес всех рёбер, входящих в него, был максимальным. |
Для решения данной задачи существует несколько алгоритмов. Например, алгоритм Куна, который имеет верхнюю оценку порядка . Но так как нам дано дерево, то можно использовать динамическое программирование, время работы алгоритма с которым улучшается до .
Обозначим как паросочетание максимального веса в поддереве с корнем в -той вершине, при этом -тая вершина соединена ребром, входящим в паросочетание, с вершиной, входящей в поддерево -ой вершины; аналогично — как паросочетание максимального веса в поддерева с корнем в -той вершине, но только при этом -тая вершина соединена ребром, входящим в паросочетание, с вершиной, не входящей в поддерево -ой вершины; а , таким образом, ответ на задачу будет находиться в , где — корень дерева. Идея динамического программирования здесь состоит в том, что для того, чтобы найти паросочетание максимального веса с корнем в вершине , нам необходимо найти максимальное паросочетание для всех поддеревьев -ой вершины.
Обозначим — как множество сыновей вершины и будем находить значения и следующим образом:
Если вершина — лист, то ,
в противном же случае
- ,
С учётом того, что , эти формулы можно переписать как
- .
Теперь оценим количество операций, необходимых нам для нахождения . Так как , то для вычисления необходимо вычислить , . Для вычисления и того, и другого необходимо время порядка , где — число вершин в дереве.
Псевдокод
// в основной процедуре вызываем dfs от корня(root), после этого ответ будет хранится в c[root]
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]) // по формуле выше, но без b[x] (прибавим его один раз в конце)
b[x] += с[i]
a[x] += b[x] // так как в a[x] пока что хранится только на сколько мы можем увеличить ответ если будем использовать вершину x
c[x] = max(a[x], b[x])
Задача о сумме длин всех путей в дереве
| Задача: |
| Найти сумму длин всех путей в дереве. |
Решим эту задачу за . Пусть задано подвешенное дерево. Рассмотрим количество путей для вершины . Во-первых, это пути не проходящие через эту вершину, то есть все пути между её сыновьями. Во-вторых, пути, которые оканчиваются вершиной . И в третьих, это пути проходящие через вершину , они начинаются из поддерева одного из сыновей этой вершины и заканчиваются в другом поддереве одного из сыновей вершины .
Теперь подсчитаем пути для каждого варианта. Обозначим размер поддерева , сумма длин всех путей вершины , количество путей оканчивающихся вершиной , количество путей проходящих через вершину . Если вершина лист, то = 1, а = 0.
- Пути не проходящие через эту вершину. Это просто сумма суммы длин для всех поддеревьев детей или .
- Пути оканчивающиеся в вершине . Рассмотрим ребро, соединяющее вершину и одного ее сына, пусть это будет вершина . Переберем все пути, которые начинаются с этого ребра и идут вниз. Это будет сумма путей оканчивающихся в , так как суммарная длина поддерева уже сосчитана и каждый такой путь мы продлили ребром, соединяющим вершины и . Всего таких путей: .
- Пути проходящие через вершину . Рассмотрим двух сыновей этой вершины: и . Нам надо подсчитать все пути, которые поднимаются из поддерева в и затем опускаются в поддерево и наоборот. То есть по каждому пути, оканчивающимся в вершине мы пройдем столько раз сколько элементов в поддереве , следовательно таких путей будет . Аналогично, если будем подниматься из поддерева . Также надо учитывать сколько раз мы проходим по ребрам, соединяющим вершины и . Итого для двух вершин и : , следовательно ( ) . Но такой подсчет испортит асимптотику до . Заметим, что . Но еще надо учесть, что , следовательно . Аналогично для . Итак: .
Ответ задачи: . Асимптотика каждого слагаемого равна , где — число вершин в дереве, следовательно и время работы самого алгоритма .
Амортизированные оценки для ДП на дереве
| Теорема: |
Пусть какой-либо алгоритм на дереве работает за время для вершины x, тогда время обработки им всего дерева не превышает : |
| Доказательство: |
| , поэтому . |