Відстань на дереві I#11430

За допомогою двох запусків пошуку в глибину знайдемо діаметр дерева.

Оскільки дерево — це зв’язний граф без циклів, як пошук в глибину (DFS), так і пошук в ширину (BFS) коректно знаходять найкоротші відстані від стартової вершини до всіх інших.

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

Визначимо найкоротші відстані від вершини до всіх інших і збережемо їх в масиві , а від вершини — у масиві .

Тоді для кожної вершини i найбільша відстань до іншої вершини буде дорівнювати

max(dista[i], distb[i])

Приклад

Розгляньмо дерево з прикладу.

Діаметром дерева може бути, наприклад, шлях між вершинами і .

Реалізація алгоритму

Вхідне дерево зберігається у вигляді списку суміжності .

Найкоротші відстані від вершин та зберігаються в масивах і .

vector<vector<int>> g;
vector<int> dista, distb;

Функція dfs виконує пошук в глибину з вершини .

void dfs(int v, int d, vector<int>& dist, int p = -1)
{

Записуємо в dist[v] найкоротшу відстань від джерела до вершини .

  dist[v] = d;

Перебираємо всі ребра (v, to). Для кожного сина вершини (вершина не є предком вершини ) запускаємо пошук в глибину.

  for (int to : g[v])
    if (to != p) dfs(to, d + 1, dist, v);
}

Основна частина програми. Читаємо вхідні дані.

scanf("%d", &n);
g.resize(n + 1);
dista.resize(n + 1);
distb.resize(n + 1);

Будуємо неорієнтоване дерево.

for (i = 1; i < n; i++)
{
  scanf("%d %d", &u, &v);
  g[u].push_back(v);
  g[v].push_back(u);
}

Знаходимо найкоротші відстані від вершини . Найвіддаленішою від неї вершиною є вершина .

dfs(1, 0, dista, -1);
a = max_element(dista.begin() + 1, dista.begin() + n + 1) - dista.begin();

Потім обчислюємо найкоротші відстані від вершини і зберігаємо їх в масиві . Найвіддаленішою вершиною від виявляється вершина . Відстань між вершинами і є діаметром дерева.

dfs(a, 0, dista, -1);
b = max_element(dista.begin() + 1, dista.begin() + n + 1) - dista.begin();

Обчислюємо найкоротші відстані від вершини і зберігаємо їх в масиві .

dfs(b, 0, distb, -1);

Для кожної вершини виводимо відстань до найвіддаленішої від неї вершини.

for (i = 1; i <= n; i++)
  printf("%d ", max(dista[i], distb[i]));
printf("\n");

Python реалізація

Збільшимо стек для рекурсії.

import sys
sys.setrecursionlimit(1000000)

Функція dfs виконує пошук в глибину з вершини .

def dfs(v, d, dist, p =- 1):

Записуємо в dist[v] найкоротшу відстань від джерела до вершини .

  dist[v] = d

Перебираємо всі ребра (v, to). Для кожного сина вершини (вершина не є предком вершини ) запускаємо пошук в глибину.

  for to in g[v]:
    if to != p:
      dfs(to, d + 1, dist, v)

Основна частина програми. Читаємо вхідні дані.

n = int(input())
g = [[] for _ in range(n + 1)]
dista = [0] * (n + 1)
distb = [0] * (n + 1)

Будуємо неорієнтоване дерево.

for _ in range(n - 1):
  u, v = map(int, input().split())
  g[u].append(v)
  g[v].append(u)

Знаходимо найкоротші відстані від вершини . Найвіддаленішою від неї вершиною є вершина .

dfs(1, 0, dista)
a = dista.index(max(dista[1:]))

Потім обчислюємо найкоротші відстані від вершини і зберігаємо їх в масиві . Найвіддаленішою вершиною від виявляється вершина . Відстань між вершинами і є діаметром дерева.

dfs(a, 0, dista)
b = dista.index(max(dista[1:]))

Обчислюємо найкоротші відстані від вершини і зберігаємо їх в масиві .

dfs(b, 0, distb)

Для кожної вершини виводимо відстань до найвіддаленішої від неї вершини.

result = [max(dista[i], distb[i]) for i in range(1, n + 1)]
print(*result)