E029. Поиск мостов

e-maxx algorithm original: C/C++ #algorithm #connectivity #emaxx #graph #search #tree
Văn bản bài toán được dịch từ tiếng Nga theo ngôn ngữ giao diện. Mã không thay đổi.

Источник: e-maxx.ru/algo, страница PDF 92.

Пусть дан неориентированный đồ thị. Мостом называется такое edge, удаление которого делает đồ thị несвязным (или, точнее, увеличивает number компонент связности). it is required find все мосты в заданном đồ thịе. Неформально эта Bài toán ставится следующим образом: it is required find на заданной карте дорог все "важные" дороги, т.е. такие дороги, что удаление любой из них приведёт к исчезновению пути между какой-то парой городов. Ниже мы опишем Thuật toán, основанный на поиске в глубину, и работающий за время

, где

количество вершин,

— рёбер в đồ thịе. Заметим, что на сайте также описан онлайновый Thuật toán поиска мостов — в отличие от описанного здесь Thuật toánа, онлайновый Thuật toán умеет поддерживать все мосты đồ thịа в изменяющемся đồ thịе (имеются в виду добавления новых рёбер).

Thuật toán

Запустим обход в глубину из произвольной вершины đồ thịа; обозначим её через

. Заметим следующий

факт (который несложно доказать):

● Пусть мы находимся в обходе в глубину, просматривая сейчас все рёбра из вершины

. Тогда, если текущее

edge

таково, что из вершины

и из любого её потомка в дереве обхода в глубину нет обратного ребра

в вершину

или какого-либо её предка, то это edge является мостом. В противном случае оно мостом не является. (В самом деле, мы этим Đề bàiм проверяем, нет ли другого пути из

в

, кроме как спуск по ребру

дерева обхода в глубину.)

Теперь осталось научиться проверять этот факт для каждой вершины эффективно. Для этого воспользуемся "временами Đầu vàoа в вершину", вычисляемыми Thuật toánом поиска в глубину.

Итак, пусть

— это время захода поиска в глубину в вершину

. Теперь введём mảng

, который

и позволит нам отвечать на вышеописанные запросы. Время

равно минимуму из времени захода в саму

вершину

, времён захода в каждую вершину

, являющуюся концом некоторого обратного ребра

, а

также из всех значений

для каждой вершины

, являющейся непосредственным сыном

в дереве поиска: (здесь "back edge" — обратное edge, "tree edge" — edge дерева)

Тогда, из вершины

или её потомка есть обратное edge в её предка тогда и только тогда, когда найдётся такой сын

, что

. (Если

, то это означает, что найдётся обратное edge,

приходящее точно в

; если же

, то это означает наличие обратного ребра в какого-либо

предка вершины

.)

Таким образом, если для текущего ребра

(принадлежащего дереву поиска) выполняется

,

то это edge является мостом; в противном случае оно мостом не является.

Cài đặt

Если говорить о самой реализации, то здесь нам нужно уметь различать три случая: когда мы идём по ребру дерева поиска в глубину, когда идём по обратному ребру, и когда пытаемся пойти по ребру дерева в обратную сторону. Это, соответственно, случаи:

— критерий ребра дерева поиска;

— критерий обратного ребра;

— критерий прохода по ребру дерева поиска в обратную сторону. Таким образом, для реализации этих критериев нам надо передавать в функцию поиска в глубину вершину-предка текущей вершины.

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);

}

Здесь основная функция для вызова — это

— она производит необходимую инициализацию и

запуск обхода в глубину для каждой компоненты связности đồ thịа.

При этом

— это некая функция, которая будет реагировать на то, что edge

является мостом, наVí dụ, выводить это edge на экран.

Константе

в самом начале кода следует задать значение, равное максимально возможному числу вершин во Đầu vàoном đồ thịе. Стоит заметить, что эта Cài đặt некорректно работает при наличии в đồ thịе кратных рёбер: она фактически не обращает внимания, кратное ли edge или оно единственно. Разумеется, кратные рёбра не должны Đầu vàoить в

ответ, поэтому при вызове

можно проверять дополнительно, не кратное ли edge мы хотим добавить в ответ. Другой способ — более аккуратная работа с предками, т.е. передавать в

не вершину-предка, а номер

ребра, по которому мы вошли в вершину (для этого надо будет дополнительно хранить номера всех рёбер).

Задачи в online judges

Список задач, в которых it is required искать мосты:

● UVA #796 "Critical Links" [Complexity: низкая]

● UVA #610 "Street Directions" [Complexity: средняя]

C# lời giải

bản nháp tự động, xem lại trước khi gửi
using 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++ lời giải

đã khớp/gốc
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);
}

Java lời giải

bản nháp tự động, xem lại trước khi gửi
import 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);
    }
}

Материал разбит как Thuật toánическая Bài toán: изучить постановку, понять асимптотику и реализовать Thuật toán на выбранном языке.

Vacancies for this task

việc làm đang hoạt động with overlapping task tags are đã hiển thị.

Tất cả việc làm
Chưa có việc làm đang hoạt động.