Погоня за метеликом#11058

Погоня за метеликом (Розбір)

Виконаємо обхід в глибину з вершини . Для кожної вершини обчислимо два значення:

Потім запустимо другий обхід в глибину. Під час цього обходу будемо обчислювати значення — мінімальна кількість друзів, яких потрібно розмістити в деяких із листків піддерева з коренем у вершині , щоб гарантовано зловити метелика, за умови, що він вибрав це піддерево.

Якщо h[v] \le d[v], то res[v] = 1. В цьому випадку достатньо розмістити одного друга в листі з мінімальною глибиною в піддереві вершини : він встигне дійти до вершини не пізніше, ніж метелик — з кореня дерева.

В іншому випадку, якщо to_1, to_2, ..., to_k — сини вершини , то

res[v] = res[to_1] + res[to_2] + ... + res[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) 

У змінній обчислюємо значення

min(h[to_1], h[to_2], ..., h[to_k]),

де to_1, to_2, ..., to_k — сини вершини .

height = min(height, dfs(to, v, cnt + 1));

Якщо height = INF, значить вершина є листом, і слід встановити h[v] = 0. В іншому випадку повертаємо

h[v] = 1 + min(h[to_1], h[to_2], ..., h[to_k])
  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 — сини вершини . У змінній обчислимо суму:

res[to_1] + res[to_2] + ... + res[to_k]
  for (int to : g[v])
    if (to != p)
    {
      dfs2(to, v);
      s += res[to];
    }

Якщо h[v] \le d[v], то достатньо одного друга, і res[v] = 1. Інакше

res[v] = res[to_1] + res[to_2] + ... + res[to_k] = s
  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]);