E029. Поиск мостов
Источник: e-maxx.ru/algo, страница PDF 92.
Пусть дан неориентированный grafo. Мостом называется такое edge, удаление которого делает grafo несвязным (или, точнее, увеличивает number компонент связности). it is required find все мосты в заданном grafoе. Неформально эта Problema ставится следующим образом: it is required find на заданной карте дорог все "важные" дороги, т.е. такие дороги, что удаление любой из них приведёт к исчезновению пути между какой-то парой городов. Ниже мы опишем Algoritmo, основанный на поиске в глубину, и работающий за время
, где
—
количество вершин,
— рёбер в grafoе. Заметим, что на сайте также описан онлайновый Algoritmo поиска мостов — в отличие от описанного здесь Algoritmoа, онлайновый Algoritmo умеет поддерживать все мосты grafoа в изменяющемся grafoе (имеются в виду добавления новых рёбер).
Algoritmo
Запустим обход в глубину из произвольной вершины grafoа; обозначим её через
. Заметим следующий
факт (который несложно доказать):
● Пусть мы находимся в обходе в глубину, просматривая сейчас все рёбра из вершины
. Тогда, если текущее
edge
таково, что из вершины
и из любого её потомка в дереве обхода в глубину нет обратного ребра
в вершину
или какого-либо её предка, то это edge является мостом. В противном случае оно мостом не является. (В самом деле, мы этим Testoм проверяем, нет ли другого пути из
в
, кроме как спуск по ребру
дерева обхода в глубину.)
Теперь осталось научиться проверять этот факт для каждой вершины эффективно. Для этого воспользуемся "временами Inputа в вершину", вычисляемыми Algoritmoом поиска в глубину.
Итак, пусть
— это время захода поиска в глубину в вершину
. Теперь введём array
, который
и позволит нам отвечать на вышеописанные запросы. Время
равно минимуму из времени захода в саму
вершину
, времён захода в каждую вершину
, являющуюся концом некоторого обратного ребра
, а
также из всех значений
для каждой вершины
, являющейся непосредственным сыном
в дереве поиска: (здесь "back edge" — обратное edge, "tree edge" — edge дерева)
Тогда, из вершины
или её потомка есть обратное edge в её предка тогда и только тогда, когда найдётся такой сын
, что
. (Если
, то это означает, что найдётся обратное edge,
приходящее точно в
; если же
, то это означает наличие обратного ребра в какого-либо
предка вершины
.)
Таким образом, если для текущего ребра
(принадлежащего дереву поиска) выполняется
,
то это edge является мостом; в противном случае оно мостом не является.
Implementazione
Если говорить о самой реализации, то здесь нам нужно уметь различать три случая: когда мы идём по ребру дерева поиска в глубину, когда идём по обратному ребру, и когда пытаемся пойти по ребру дерева в обратную сторону. Это, соответственно, случаи:
●
— критерий ребра дерева поиска;
●
— критерий обратного ребра;
●
— критерий прохода по ребру дерева поиска в обратную сторону. Таким образом, для реализации этих критериев нам надо передавать в функцию поиска в глубину вершину-предка текущей вершины.
const int MAXN = ...;
vector<int> g[MAXN];
bool used[MAXN];
int timer, tin[MAXN], fup[MAXN];
void dfs (int v, int p = -1) {
used[v] = true;
tin[v] = fup[v] = timer++;
for (size_t i=0; i<g[v].size(); ++i) {
int to = g[v][i];
if (to == p) continue;
if (used[to])
fup[v] = min (fup[v], tin[to]);
else {
dfs (to, v);
fup[v] = min (fup[v], fup[to]);
if (fup[to] > tin[v])
IS_BRIDGE(v,to);
} } }
void find_bridges() {
timer = 0;
for (int i=0; i<n; ++i)
used[i] = false;
for (int i=0; i<n; ++i)
if (!used[i])
dfs (i);
}
Здесь основная функция для вызова — это
— она производит необходимую инициализацию и
запуск обхода в глубину для каждой компоненты связности grafoа.
При этом
— это некая функция, которая будет реагировать на то, что edge
является мостом, наEsempio, выводить это edge на экран.
Константе
в самом начале кода следует задать значение, равное максимально возможному числу вершин во Inputном grafoе. Стоит заметить, что эта Implementazione некорректно работает при наличии в grafoе кратных рёбер: она фактически не обращает внимания, кратное ли edge или оно единственно. Разумеется, кратные рёбра не должны Inputить в
ответ, поэтому при вызове
можно проверять дополнительно, не кратное ли edge мы хотим добавить в ответ. Другой способ — более аккуратная работа с предками, т.е. передавать в
не вершину-предка, а номер
ребра, по которому мы вошли в вершину (для этого надо будет дополнительно хранить номера всех рёбер).
Задачи в online judges
Список задач, в которых it is required искать мосты:
● UVA #796 "Critical Links" [Complexity: низкая]
● UVA #610 "Street Directions" [Complexity: средняя]
C# soluzione
bozza automatica, rivedere prima dell'inviousing System;
using System.Collections.Generic;
using System.Linq;
public static class AlgorithmDraft
{
// Auto-generated C# draft from the original e-maxx C/C++ listing. Review before production use.
const int MAXN = ...;
List<int> g[MAXN];
bool[] used = new bool[MAXN];
int timer, tin[MAXN], fup[MAXN];
void dfs (int v, int p = -1) {
used[v] = true;
tin[v] = fup[v] = timer++;
for (size_t i=0; i<g[v].size(); ++i) {
int to = g[v][i];
if (to == p) continue;
if (used[to])
fup[v] = min (fup[v], tin[to]);
else {
dfs (to, v);
fup[v] = min (fup[v], fup[to]);
if (fup[to] > tin[v])
IS_BRIDGE(v,to);
}
}
}
void find_bridges() {
timer = 0;
for (int i=0; i<n; ++i)
used[i] = false;
for (int i=0; i<n; ++i)
if (!used[i])
dfs (i);
}
}
C++ soluzione
abbinato/originaleconst int MAXN = ...;
vector<int> g[MAXN];
bool used[MAXN];
int timer, tin[MAXN], fup[MAXN];
void dfs (int v, int p = -1) {
used[v] = true;
tin[v] = fup[v] = timer++;
for (size_t i=0; i<g[v].size(); ++i) {
int to = g[v][i];
if (to == p) continue;
if (used[to])
fup[v] = min (fup[v], tin[to]);
else {
dfs (to, v);
fup[v] = min (fup[v], fup[to]);
if (fup[to] > tin[v])
IS_BRIDGE(v,to);
}
}
}
void find_bridges() {
timer = 0;
for (int i=0; i<n; ++i)
used[i] = false;
for (int i=0; i<n; ++i)
if (!used[i])
dfs (i);
}
Java soluzione
bozza automatica, rivedere prima dell'invioimport java.util.*;
import java.math.*;
public class AlgorithmDraft {
// Auto-generated Java draft from the original e-maxx C/C++ listing. Review before production use.
const int MAXN = ...;
ArrayList<Integer> g[MAXN];
boolean used[MAXN];
int timer, tin[MAXN], fup[MAXN];
void dfs (int v, int p = -1) {
used[v] = true;
tin[v] = fup[v] = timer++;
for (size_t i=0; i<g[v].size(); ++i) {
int to = g[v][i];
if (to == p) continue;
if (used[to])
fup[v] = min (fup[v], tin[to]);
else {
dfs (to, v);
fup[v] = min (fup[v], fup[to]);
if (fup[to] > tin[v])
IS_BRIDGE(v,to);
}
}
}
void find_bridges() {
timer = 0;
for (int i=0; i<n; ++i)
used[i] = false;
for (int i=0; i<n; ++i)
if (!used[i])
dfs (i);
}
}
Материал разбит как Algoritmoическая Problema: изучить постановку, понять асимптотику и реализовать Algoritmo на выбранном языке.
Vacancies for this task
offerte attive with overlapping task tags are mostrati.