Виконаємо обхід в глибину з вершини . Для кожної вершини обчислимо два значення:
— відстань від вершини до вершини . Якщо — батько , то
— відстань від вершини до найближчого листа в піддереві з коренем . Якщо to_1, to_2, ..., to_k — сини вершини , то
Якщо — лист дерева, то покладемо h[v] = 0.
Потім запустимо другий обхід в глибину. Під час цього обходу будемо обчислювати значення — мінімальна кількість друзів, яких потрібно розмістити в деяких із листків піддерева з коренем у вершині , щоб гарантовано зловити метелика, за умови, що він вибрав це піддерево.
Якщо h[v] \le d[v], то res[v] = 1. В цьому випадку достатньо розмістити одного друга в листі з мінімальною глибиною в піддереві вершини : він встигне дійти до вершини не пізніше, ніж метелик — з кореня дерева.
В іншому випадку, якщо to_1, to_2, ..., to_k — сини вершини , то
Якщо метелик не буде спійманий в самій вершині , необхідно бути готовими перехопити його в будь-якому із піддерев, що виходять із синів .
Шуканою відповіддю на задачу є значення .
Приклад
У першому прикладі (на малюнку зліва) потрібні два друга, яких необхідно розмістити у двох виходах. Метелик може полетіти з вершини або в вершину , або в вершину . В будь-якому з цих випадків його перехопить один із друзів Нюші.
У другому прикладі (на малюнку справа) достатньо одного друга, якого можна розмістити в будь-якому з двох виходів — вершині або . За одну хвилину метелик долетить з вершини до вершини . За цей же час друг добереться до вершини і зловить метелика там.
Розглянемо наступний приклад. Для поимки метелика Нюше знадобляться три друга.
Реалізація алгоритму
Оголосимо константу нескінченність та робочі масиви.
#define INF 2000000000
vector<vector<int> > g;
vector<int> d, h, res;Функція dfs (v, p, cnt) виконує обхід в глибину, починаючи з вершини , і повертає значення . При цьому — предок вершини , а — відстань від вершини до . В процесі обходу для кожної вершини обчислюються значення та .
int dfs(int v, int p = -1, int cnt = 0)
{Значення , рівне відстані від вершини до , зберігаємо в .
d[v] = cnt;
int height = INF;Перебираємо всіх синів вершини . Розглядаємо ребро v \to to. Якщо збігається з предком v~(to = p), переходимо до наступного сина.
for (int to : g[v])
if (to != p) У змінній обчислюємо значення
де to_1, to_2, ..., to_k — сини вершини .
Якщо height = INF, значить вершина є листом, і слід встановити h[v] = 0. В іншому випадку повертаємо
return h[v] = (height == INF) ? 0 : 1 + height;
}Функція dfs2 виконує обхід в глибину, починаючи з вершини , і повертає значення . Тут — предок вершини .
int dfs2(int v, int p = -1)
{
int s = 0;Нехай to_1, to_2, ..., to_k — сини вершини . У змінній обчислимо суму:
for (int to : g[v])
if (to != p)
{
dfs2(to, v);
s += res[to];
}Якщо h[v] \le d[v], то достатньо одного друга, і res[v] = 1. Інакше
return res[v] = (h[v] <= d[v]) ? 1 : s;
}Основна частина програми. Читаємо вхідні дані. Будуємо граф.
scanf("%d", &n);
g.resize(n + 1);
for (i = 0; i < n - 1; i++)
{
scanf("%d %d", &a, &b);
g[a].push_back(b);
g[b].push_back(a);
}Ініціалізуємо масиви.
d.resize(n + 1);
h.resize(n + 1);
res.resize(n + 1);Запускаємо два обходи в глибину. Перший обхід для кожної вершини обчислює значення та .
dfs(1);
dfs2(1);Виводимо відповідь — мінімальна кількість друзів, необхідна для поимки метелика.
printf("%lld\n", res[1]);