Каков хороший алгоритм для получения минимального вершинного покрытия дерева?
INPUT:
Соседи node.
ВЫВОД:
Минимальное количество вершин.
Каков хороший алгоритм для получения минимального вершинного покрытия дерева?
Соседи node.
Минимальное количество вершин.
Я надеюсь, что здесь вы можете найти более близкий ответ на свой вопрос.
Я думал о своем решении, возможно, вам нужно будет отполировать его, но пока динамическое программирование находится в одном из ваших тегов, вам, вероятно, нужно:
После прочтения этого. Изменен алгоритм выше, чтобы найти максимальный независимый набор, поскольку в статье wiki указано
Множество является независимым тогда и только тогда, когда его дополнение является вершинным накрытием.
Таким образом, изменяя min на max, мы можем найти максимальное независимое множество и дополнять минимальное покрытие вершин, так как обе задачи эквивалентны.
T (V, E) - дерево, из которого следует, что для любого листа любое минимальное покрытие вершин должно включать либо лист, либо вершину, смежную с листом. Это дает нам следующий алгоритм нахождения S: покрытие вершины:
Теперь остается только проверить, что если исходное дерево имеет только одну вершину, мы возвращаем 1 и никогда не начинаем рекурсию, и можно вычислить минимальное покрытие вершин.
Edit:
На самом деле, немного подумав об этом, его можно выполнить с помощью простого варианта DFS.
Я не совсем понял после прочтения ответов здесь, поэтому я подумал, что отправлю сообщение из здесь
Общая идея заключается в том, что вы создаете дерево на произвольном node и спрашиваете, находится ли этот корень в обложке или нет. Если это так, то вы вычисляете минимальное покрытие вершин поддеревьев, укорененных на своих дочерних элементах, путем рекурсии. Если это не так, то каждый ребенок корня должен находиться в крышке вершины, чтобы покрыть каждое ребро между корнем и его дочерними элементами. В этом случае вы возвращаетесь к корням внуков.
Так, например, если у вас было следующее дерево:
A
/ \
B C
/ \ / \
D E F G
Обратите внимание, что при проверке вы знаете, что минимальная крышка вершины {B, C}
. Мы найдем эту минимальную обложку.
Здесь мы начинаем с A
.
Мы переходим к двум поддеревьям B
и C
и рекурсируем по этому алгоритму. Мы не можем просто заявить, что B
и C
не находятся в обложке, потому что даже если AB
и AC
покрыты, мы ничего не можем сказать о том, нужны ли нам B
и C
быть в обложке или нет.
(Подумайте о следующем дереве, где и корень, и один из его дочерних элементов находятся в обложке min ({A, D}
)
A
/|\___
B C D
/|\
E F G
)
Но мы знаем, что AB
и AC
должны быть покрыты, поэтому нам нужно добавить B
и C
к обложке. Поскольку B
и C
находятся в обложке, мы можем перезаписывать их дочерние элементы вместо рекурсии на B
и C
(даже если бы мы это сделали, это не предоставило бы нам больше информации).
Пусть C(x)
- размер минимальной обложки, основанной на x
.
Затем
C(x) = min (
1 + sum ( C(i) for i in x children ), // root in cover
len(x children) + sum( C(i) for i in x grandchildren) // root not in cover
)
{- Haskell implementation of Artem algorithm -}
data Tree = Branch [Tree]
deriving Show
{- first int is the min cover; second int is the min cover that includes the root -}
minVC :: Tree -> (Int, Int)
minVC (Branch subtrees) = let
costs = map minVC subtrees
minWithRoot = 1 + sum (map fst costs) in
(min minWithRoot (sum (map snd costs)), minWithRoot)
Мы можем использовать алгоритм, основанный на DFS, для решения этой задачи:
DFS(node x)
{
discovered[x] = true;
/* Scan the Adjacency list for the node x*/
while((y = getNextAdj() != NULL)
{
if(discovered[y] == false)
{
DFS(y);
/* y is the child of node x*/
/* If child is not selected as a vertex for minimum selected cover
then select the parent */
if(y->isSelected == false)
{
x->isSelected = true;
}
}
}
}
Лист node никогда не будет выбран для крышки вершин.
Я бы просто использовал линейную программу для решения проблемы минимального покрытия вершин. Формула как целочисленная линейная программа может выглядеть так, как показано здесь: формулировка ILP
Я не думаю, что ваша собственная реализация была бы быстрее, чем эти высоко оптимизированные решатели LP.
Нам нужно найти минимальное покрытие вершин для каждого node, которое мы должны выбрать, чтобы включить его или нет, чтобы включить его. Но в соответствии с проблемой для каждого ребра (u, v) в обложке должно быть либо "u", либо "v", поэтому нам нужно позаботиться о том, чтобы, если текущая вершина не включена, мы должны включить ее дочерние элементы и если мы включаем текущую вершину, то мы можем или не можем включать своих детей на основе оптимального решения.
Здесь DP1 [v] для любой вершины v = Когда мы ее включаем. DP2 [v] для любой вершины v =, когда мы ее не включаем.
DP1 [v] = 1 + sum (min (DP2 [c], DP1 [c])) - это означает включение тока и может включать или не включать его детей на основе оптимального.
DP2 [v] = sum (DP1 [c]) - это означает, что не включает ток, тогда нам нужно включить дочерние элементы текущей вершины. Здесь c - дочерний элемент вершины v.
Тогда наше решение равно min (DP1 [root], DP2 [root])
#include <bits/stdc++.h>
using namespace std;
vector<vector<int> > g;
int dp1[100010], dp2[100010];
void dfs(int curr, int p){
for(auto it : g[curr]){
if(it == p){
continue;
}
dfs(it, curr);
dp1[curr] += min(dp1[it], dp2[it]);
dp2[curr] += dp1[it];
}
dp1[curr] += 1;
}
int main(){
int n;
cin >> n;
g.resize(n+1);
for(int i=0 ; i<n-1 ; i++){
int u, v;
cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
dfs(1, 0);
cout << min(dp1[1], dp2[1]);
return 0;
}