Диаметр дерева
Два обхода вместо перебора всех пар. Алгоритм в пять строк, доказательство длиннее алгоритма — и почему в общем графе так нельзя.
3 мин
Диаметр дерева — наибольшее расстояние между какой-либо парой вершин.
Наивно: перебрать все пары и найти расстояние — . Лучше: запустить обход из каждой вершины — . Но есть решение за , и оно неожиданно короткое.
Алгоритм
- Возьмите произвольную вершину .
- Обходом найдите самую далёкую от неё вершину .
- Обходом найдите самую далёкую от вершину .
Расстояние между и — это диаметр.
pair<int, int> farthest(int start) { // {вершина, расстояние}
vector<int> dist(n, -1);
dist[start] = 0;
queue<int> q;
q.push(start);
int best = start;
while (!q.empty()) {
int v = q.front(); q.pop();
if (dist[v] > dist[best]) best = v;
for (int to : g[v]) if (dist[to] == -1) { dist[to] = dist[v] + 1; q.push(to); }
}
return {best, dist[best]};
}
int diameter() {
int a = farthest(0).first;
return farthest(a).second;
}
Два обхода, . Проверено: на двадцати тысячах случайных деревьев до двенадцати вершин результат совпал с перебором всех стартовых вершин.
Почему это верно
Утверждение, которое нужно доказать: самая далёкая вершина от любой вершины является концом какого-то диаметра.
Подвесим дерево за и посмотрим на найденную вершину .
Шаг 1: — лист. Иначе у неё есть ребёнок, который дальше от , — противоречие с тем, что самая далёкая.
Шаг 2. Пусть диаметр — это путь между и , и ни один из концов не совпадает с . Путь в подвешенном дереве — это подъём до наименьшего общего предка и и спуск обратно; обозначим этот предок .
Путь от до тоже где-то проходит. Возможны два случая.
Если путь пересекает путь , пусть точка пересечения — . Тогда расстояние от до не меньше расстояния от до и до : иначе не была бы самой далёкой от . Значит, заменив на , мы не укоротим путь, и — конец диаметра.
Если пути не пересекаются, соединим их: получится путь длиннее диаметра, что невозможно.
Отсюда: — конец какого-то диаметра, и самая далёкая от неё вершина — второй конец.
Замечание про единственность: диаметров может быть много. Алгоритм найдёт какой-то один, и его длину — а она у всех одинакова.
В общем графе так нельзя
В произвольном графе диаметр — наибольшее из кратчайших расстояний между парами. Два обхода тут не работают, и быстрого алгоритма не известно.
Причина глубокая: умей мы быстро считать диаметр, мы умели бы проверять, равен ли он , — а это существование гамильтонова пути, задача NP-полная.
На практике диаметр графа считают алгоритмом Флойда за или обходами в ширину за . Ничего лучше в общем случае нет.
Родственные задачи
Центр дерева — вершина, минимизирующая расстояние до самой далёкой. Она лежит на середине диаметра: находим диаметр, идём по нему до середины. Центров один или два, в зависимости от чётности длины.
Радиус — расстояние от центра до самой далёкой вершины, равен .
Диаметр через динамику. Подвешиваем дерево и для каждой вершины считаем две наибольшие глубины среди детей. Их сумма — длиннейший путь, проходящий через эту вершину; максимум по всем вершинам и есть диаметр. Тоже , зато обобщается на взвешенные рёбра и на задачи, где нужен не только ответ, но и путь.
Взвешенное дерево. Приём с двумя обходами работает и там, если веса неотрицательны. С отрицательными весами он ломается, и это стандартный контрпример к «а я слышал, что диаметр ищется двумя обходами».