Pull to refresh

Мосты и точки сочленения

Мосты

Определение

Пусть дан неориентированный граф. Мостом называется такое ребро, удаление которого делает граф несвязным (или, точнее, увеличивает число компонент связности). Требуется найти все мосты в заданном графе.

Неформально, эта задача ставится следующим образом: требуется найти на карте такие дороги, при удалении которых пропадает путь между какими-либо двумя точками.

Суть алгоритма

Идея

Мы можем заметить тот факт, что ребро между точками v и u является мостом тогда и только тогда, когда из вершины u и её потомков нельзя вернуться в вершину v или подняться выше нее. Осталось лишь понять, как нам это выяснить.

Алгоритм

Разобьём ребра на ориентированные и будем хранить их в отдельной структуре. Пусть мы будем запоминать номер вершины, в которую направлено ребро, номер данного ребра (номер ребра равен номеру обратного ребра) и ссылку на его обратное ребро.

Заведём граф ссылок на рёбра и заполним его.

Запустим обход графа в глубину (DFS).

Будем для каждой вершины v хранить два значения:

  • dp[v] - минимально возможная вершина в которую мы можем опуститься из этой вершины в процессе обхода в глубину (по умолчанию текущая глубина)

  • d[v] - глубина текущей вершины

В процессе обхода:

  • Если встречаем вершину u, в которой еще не были, то спускаемся в неё и тогда dp[v] = min(dp[v], dp[u]). Тем самым проверяем опустились ли мы из вершины u и её потомков выше v.

  • Если встречаем вершину v, в которой уже были, то dp[v] = min(dp[v], d[u]). Тем самым проверяем не встретили ли мы вершину выше по обходу графа, чем вершина v.

По завершении обхода вершины v и её потомков:

Если из вершины v нельзя опуститься выше нее самой и мы знаем проверяемое ребро (dp[v] == d[v] && p != nullptr(текущее ребро)), то ребро из этой вершины - есть мост.

Тогда:

p->is_bridge = true - вершина v

p->bck->is_bridge = true - вершина другого конца ребра

Реализация

#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
#define fast_cin() ios_base::sync_with_stdio(false); cin.tie(NULL); cout.tie(NULL)

struct Edge{
    ll v,num;
    bool is_bridge;
    Edge* bck;
    Edge(){}
    Edge(ll v, ll num):v(v),num(num){}
};

vector<vector<Edge*>> gr;
vector<ll> dp,d,bridges;
vector<bool> used;

void dfs(ll v, ll depth = 0, Edge* p = nullptr){
    used[v] = true;
    dp[v] = d[v] = depth;
    for(auto u : gr[v]){
        if(!used[u->v]){
            dfs(u->v,depth+1,u);
            dp[v] = min(dp[v],dp[u->v]);
        }
        else if (p && p->num != u->num){
            dp[v] = min(dp[v],d[u->v]);
        }
    }
    if(p != nullptr && dp[v] == d[v]){
        p->is_bridge = true;
        p->bck->is_bridge = true;
        bridges.push_back(p->num+1);
    }
}

int main(){
    fast_cin();
    ll n,m;
    cin >> n >> m;
    gr.resize(n);
    dp.resize(n);
    d.resize(n);
    used.resize(n);
    ll v,u;
    for(ll i = 0;i < m;i++){
        cin >> v >> u;
        v--;
        u--;
        Edge* a = new Edge(u,i);
        Edge* b = new Edge(v,i);
        a->bck = b;
        b->bck = a;
        gr[v].push_back(a);
        gr[u].push_back(b);
    }
    for(ll i = 0;i < n;i++)
        if(!used[i])
            dfs(i);

    sort(bridges.begin(),bridges.end());

    cout << bridges.size() << endl;
    for(auto i : bridges){
        cout << i << " ";
    }
}

Задачи по теме

https://informatics.mccme.ru/mod/statements/view.php?id=31311#1

https://informatics.mccme.ru/mod/statements/view.php?id=40111&chapterid=389#

https://informatics.mccme.ru/mod/statements/view.php?id=28842#1

https://informatics.mccme.ru/mod/statements/view.php?id=28842&chapterid=3586#1


Точки сочленения

Определение

Точкой сочленения называется вершина, удаление которой делает граф несвязным.

Суть алгоритма

Идея

Мы можем заметить два факта:

  • Рассмотрим ребро между вершинами v и u. Тогда если из вершины u и ее потомков нельзя попасть в какого-либо предка вершины v и притом вершина v не является корнем дерева, то данная вершина и есть точка сочленения

  • Если вершина v - корень дерева, то она является точкой сочленения тогда и только тогда, когда эта точка имеет более одного сына в обходе графа в глубину

Алгоритм

Заведем общий счетчик времени входа timer и два массива:

  • tin[v] - время входа в вершину v

  • fup[v] - минимально достижимая вершина из вершины v

Запустим обход графа в глубину (DFS).

В процессе обхода:

  • Если встречаем уже посещенную вершину u, то fup[v] = min(fup[v], tin[u]). Проверяем не спустились ли мы выше v.

  • Если встречаем не посещенную вершину v, то спускаемся в неё. Затем fup[v] = min(fup[v], fup[u]). Тем самым проверим не спустились ли мы выше v из вершины u и ее потомков. И наконец если из вершины u нельзя спуститься ниже вершины v и вершины v - не корень дерева обхода графа (v fup[u] >= tin[v] && p != -1), то вершина v и есть искомая точка сочленения. Добавляем 1 к количеству сыновей вершины v (children++)

По завершении обхода:

Если вершина v является корнем обхода графа в глубину и имеет более одного сына, то она также является точкой сочленения (p == -1 && children > 1)

Реализация

#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
#define fast_cin() ios_base::sync_with_stdio(false); cin.tie(NULL); cout.tie(NULL)
ll timer = 0;
vector<vector<ll>> gr;
vector<ll> tin,fup;
set<ll> points_of_connectebility;
vector<bool> used;

void dfs(ll v,ll p = -1){
    used[v] = true;
    tin[v] = fup[v] = timer++;
    ll children = 0;
    for(auto u : gr[v]){
        if(u == p){
            continue;
        }
        if(used[u]){
            fup[v] = min(fup[v],tin[u]);
        }
        else{
            dfs(u,v);
            fup[v] = min(fup[v],fup[u]);
            if(fup[u] >= tin[v] && p != -1){
                points_of_connectebility.insert(v+1);
            }
            children++;
        }
    }
    if(p == -1 && children > 1){
        points_of_connectebility.insert(v+1);
    }
}

int main() {
    fast_cin();
    ll n, m;
    cin >> n >> m;
    gr.resize(n);
    tin.resize(n);
    fup.resize(n);
    used.resize(n);
    ll v, u;
    for (ll i = 0; i < m; i++) {
        cin >> v >> u;
        v--;
        u--;
        gr[v].push_back(u);
        gr[u].push_back(v);
    }

    for (ll i = 0; i < n; i++)
        if (!used[i])
            dfs(i);

    cout << points_of_connectebility.size() << endl;
    for(auto i : points_of_connectebility){
        cout << i << " ";
    }
}

Задачи по теме

https://informatics.mccme.ru/mod/statements/view3.php?id=40111&chapterid=111690#1

https://informatics.mccme.ru/mod/statements/view.php?id=40111&chapterid=111795#1

https://informatics.mccme.ru/mod/statements/view.php?id=40111&chapterid=3883#1

https://informatics.mccme.ru/mod/statements/view.php?id=40111&chapterid=3586#1

Tags:
Hubs:
You can’t comment this publication because its author is not yet a full member of the community. You will be able to contact the author only after he or she has been invited by someone in the community. Until then, author’s username will be hidden by an alias.