Построение компонент вершинной двусвязности — различия между версиями

Материал из Викиконспекты
Перейти к: навигация, поиск
м (rollbackEdits.php mass rollback)
 
Строка 1: Строка 1:
{| class="wikitable" align="center" style="color: red; background-color: black; font-size: 56px; width: 800px;"
 
|+
 
|-align="center"
 
|'''НЕТ ВОЙНЕ'''
 
|-style="font-size: 16px;"
 
|
 
24 февраля 2022 года российское руководство во главе с Владимиром Путиным развязало агрессивную войну против Украины. В глазах всего мира это военное преступление совершено от лица всей страны, всех россиян.
 
 
Будучи гражданами Российской Федерации, мы против своей воли оказались ответственными за нарушение международного права, военное вторжение и массовую гибель людей. Чудовищность совершенного преступления не оставляет возможности промолчать или ограничиться пассивным несогласием.
 
 
Мы убеждены в абсолютной ценности человеческой жизни, в незыблемости прав и свобод личности. Режим Путина — угроза этим ценностям. Наша задача — обьединить все силы для сопротивления ей.
 
 
Эту войну начали не россияне, а обезумевший диктатор. И наш гражданский долг — сделать всё, чтобы её остановить.
 
 
''Антивоенный комитет России''
 
|-style="font-size: 16px;"
 
|Распространяйте правду о текущих событиях, оберегайте от пропаганды своих друзей и близких. Изменение общественного восприятия войны - ключ к её завершению.
 
|-style="font-size: 16px;"
 
|[https://meduza.io/ meduza.io], [https://www.youtube.com/c/popularpolitics/videos Популярная политика], [https://novayagazeta.ru/ Новая газета], [https://zona.media/ zona.media], [https://www.youtube.com/c/MackNack/videos Майкл Наки].
 
|}
 
 
 
==Двупроходный алгоритм==
 
==Двупроходный алгоритм==
 
Найти [[Отношение вершинной двусвязности|компоненты вершинной двусвязности]] неориентированного графа можно с помощью [[Обход_в_глубину,_цвета_вершин |обхода в глубину]].
 
Найти [[Отношение вершинной двусвязности|компоненты вершинной двусвязности]] неориентированного графа можно с помощью [[Обход_в_глубину,_цвета_вершин |обхода в глубину]].

Текущая версия на 19:43, 4 сентября 2022

Двупроходный алгоритм

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

Первый проход: ищем точки сочленения с помощью обхода в глубину, заполняем массивы [math]tin[/math] и [math]up[/math].

Второй проход: точка сочленения принадлежит как минимум двум компонентам вершинной двусвязности. Вершина [math] v \ne root [/math] является точкой сочленения, если у нее есть сын [math] u[/math], такой что [math] up[u] \geqslant tin[v] [/math].
Это также значит, что ребро [math] vu [/math] содержится в другой компоненте вершинной двусвязности, нежели ребро по которому мы пришли в вершину [math] v [/math] , используя поиск в глубину. Получается, что перейдя по этому ребру, мы окажемся в другой компоненте вершинной двусвязности.
Используем это свойство, чтобы окрасить компоненты вершинной двусвязности в различные цвета.

Псевдокод второго прохода

  • Во время первого запуска [math]dfs[/math] будут заполняться массивы [math]tin[/math] и [math]up[/math], поэтому при запуске функции [math]paint[/math] мы считаем, что они уже посчитаны.
  • [math]\mathtt{maxColor}[/math] изначально равен [math]0[/math], что эквивалентно тому, что никакое ребро не окрашено.
  • [math]\mathtt{color}[/math] хранит в себе цвет, компоненты, из которой вызвалась функция [math]\mathrm{paint}[/math] для текущей вершины.
  • [math]\mathtt{parent}[/math] — это вершина, из которой мы попали в текущую.
function paint([math]v[/math], color, parent):
  visited[[math]v[/math]] = true
  for [math] (v, u) \in E[/math]:
    if [math]u[/math] == parent
      continue
    if not visited[[math]u[/math]]
      if up[[math]u[/math]] [math]\geqslant[/math] tin[[math]v[/math]]
        newColor = ++maxColor
        col[[math]vu[/math]] = newColor
        paint([math]u[/math], newColor, [math]v[/math])
      else
        col[[math]vu[/math]] = color
        paint([math]u[/math], color, [math]v[/math])
    else if tin[[math]u[/math]] [math]\lt [/math] tin[[math]v[/math]]
      col[[math]vu[/math]] = color
function solve():
  for [math] v \in V[/math]:
    dfs([math]v[/math])    
  for [math] v \in V[/math]:
    if not visited[[math]v[/math]]
      maxColor++
      paint([math]v[/math], maxColor, -1)

Ребра каждой из компонент вершинной двусвязности окажутся окрашенными в свой цвет.
В алгоритме выполняется два прохода [math]dfs[/math], каждый из которых работает [math]O(|V| + |E|)[/math]. Значит время работы алгоритма [math]O(|V| + |E|)[/math].

Однопроходный алгоритм

Заведем стек, в который будем записывать все дуги в порядке их обработки. Если обнаружена точка сочленения, дуги очередного блока окажутся в этом стеке, начиная с дуги дерева обхода, которая привела в этот блок, до верхушки стека.
Таким образом, каждый раз находя компоненту вершинной двусвязности мы сможем покрасить все ребра, содержащиеся в ней, в новый цвет.

Доказательство корректности алгоритма

Предположим, что граф содержит точку сочленения [math] i' \in V [/math] , за которой следует один или несколько блоков. Вершины из этих блоков образуют подмножество [math] V' \subset V [/math]. В таком случае:

  1. Все вершины [math] V' [/math] являются потомками [math] i' [/math] в дереве обхода;
  2. Все вершины [math] V' [/math] будут пройдены в течение периода серого состояния [math] i' [/math];
  3. В [math] G [/math] не может быть обратных дуг из [math] V' [/math] в [math] V \setminus V' [/math].

Значит все дуги [math] V' [/math] будут будут добавлены в стек после дуги ведущей из точки сочленения в блок. В стеке в момент обнаружения точки сочленения будут находиться только дуги блока, связанного с ней, т.к. блоки найденные до него (если таковые имеются) будет уже извлечены из стека и покрашены в свой цвет.

Псевдокод

function paint([math]v[/math], parent):
  visited[[math]v[/math]] = true
  tin[[math]v[/math]] = up[[math]v[/math]] = time++
  for [math] (v, u) \in E[/math]:
    if [math]u[/math] == parent 
      continue
    if not visited[[math]u[/math]]
      stack.push([math]vu[/math])
      paint([math]u, v[/math])
      if up[[math]u[/math]] [math]\geqslant[/math] tin[[math]v[/math]]
        color = maxColor++
        while stack.top() != ([math]vu[/math])
          colors[stack.top()] = color
          stack.pop()
        colors[[math]vu[/math]] = color
        stack.pop()
      if up[[math]u[/math]] < up[[math]v[/math]]
        up[[math]v[/math]] = up[[math]u[/math]]
    else if tin[[math]u[/math]] < tin[[math]v[/math]] 
      stack.push([math]vu[/math])
      if tin[[math]u[/math]] < up[[math]v[/math]]
        up[[math]v[/math]] = tin[[math]u[/math]]
    else if up[[math]v[/math]] > tin[[math]u[/math]]
      up[[math]v[/math]] = up[[math]u[/math]]
function solve():
  for [math] v \in V[/math]:
    dfs([math]v[/math])
  for [math] v \in V[/math]:
    if not visited[[math]v[/math]]:
      time = 0
      maxColor++
      paint([math]v[/math], -1)

Во время алгоритма совершается один проход [math]dfs[/math], который работает за [math]O(|V| + |E|)[/math]. Внутри него совершается еще цикл, который суммарно выполняет [math]O(|E|)[/math] операций, т.к. каждое ребро может быть добавлено в стек только один раз. Следовательно, общее время работы алгоритма [math]O(|V| + |E|) + O(|E|) = O(|V| + |E|)[/math]

См. также

Источники информации