Для кожної вершини обчислимо кількість вершин у піддереві, де вона є коренем. Якщо to_1, to_2, ..., to_k — сини вершини , то
Якщо вершина є листком дерева, то sum[v] = 1.
Нехай дорівнює кількості врятованих вершин у піддереві з коренем , якщо на даний момент вершина (і тільки вона в піддереві) заражена.
Якщо вершина має тільки одного сина , то dp[v] = sum[to] - 1 (видалена вершина не є врятованою).
Нехай вершина має двох синів to_1 і to_2 (кожна вершина має ступінь не більше ). Тоді у нас є два варіанти:
Видалити to_1 і рекурсивно рятувати вершини піддерева з коренем to_2. Кількість врятованих вершин складе sum[to_1] - 1 + dp[to_2].
Видалити to_2 і рекурсивно рятувати вершини піддерева з коренем to_1. Кількість врятованих вершин складе sum[to_2] - 1 + dp[to_1].
З двох варіантів слід вибрати той, при якому кількість врятованих вершин буде більшою.
Розглянемо процес пошуку в глибину, коли розглядається ребро (v, to).
Нехай — другий син вершини . Нам слід обчислити значення dp[to] + sum[a] - 1. Спробуємо його знайти, використовуючи тільки вершини і . Нам відомо, що
Звідки
Отже
Перебираємо синів вершини і обчислюємо
Приклад
У першому прикладі Хусейн зможе врятувати дві вершини і , обравши своїм першим ходом вершину номер .
Розглянемо другий приклад. Біля кожної вершини поставимо мітки sum[v] / dp[v]. Вершини, обрані Хусейном, відобразимо зеленим кольором.
Наприклад,
Реалізація алгоритму
Оголосимо робочі масиви.
#define MAX 300005
int sum[MAX], dp[MAX];
vector<vector<int>> g;Функція dfs здійснює пошук в глибину з вершини . Предком вершини є .
void dfs(int v, int p = -1)
{Ініціалізуємо значення і .
sum[v] = 1;
dp[v] = 0;Запускаємо пошук в глибину з синів вершини .
for (int to : g[v])
if (to != p)
{
dfs(to, v);
sum[v] += sum[to];
}Вичисляємо значення .
for (int to : g[v])
if (to != p)
{
dp[v] = max(dp[v], sum[to] - 1); // if 1 son
dp[v] = max(dp[v], dp[to] + sum[v] - sum[to] - 2);
}
}Основна частина програми. Читаємо вхідні дані.
scanf("%d", &n);
g.clear(); g.resize(n + 1);
memset(dp, 0, sizeof(dp));
memset(sum, 0, sizeof(sum));
for (i = 1; i < n; i++)
{
scanf("%d %d", &u, &v);
g[u].push_back(v);
g[v].push_back(u);
}Запускаємо пошук в глибину з вершини .
dfs(1);Виводимо відповідь.
printf("%d\n", dp[1]);