<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="ru">
		<id>http://neerc.ifmo.ru/wiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=94.29.126.242&amp;*</id>
		<title>Викиконспекты - Вклад участника [ru]</title>
		<link rel="self" type="application/atom+xml" href="http://neerc.ifmo.ru/wiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=94.29.126.242&amp;*"/>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A1%D0%BB%D1%83%D0%B6%D0%B5%D0%B1%D0%BD%D0%B0%D1%8F:%D0%92%D0%BA%D0%BB%D0%B0%D0%B4/94.29.126.242"/>
		<updated>2026-05-09T05:35:21Z</updated>
		<subtitle>Вклад участника</subtitle>
		<generator>MediaWiki 1.30.0</generator>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%BE%D1%81%D1%82%D1%80%D0%BE%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BA%D0%BE%D0%BC%D0%BF%D0%BE%D0%BD%D0%B5%D0%BD%D1%82_%D0%B2%D0%B5%D1%80%D1%88%D0%B8%D0%BD%D0%BD%D0%BE%D0%B9_%D0%B4%D0%B2%D1%83%D1%81%D0%B2%D1%8F%D0%B7%D0%BD%D0%BE%D1%81%D1%82%D0%B8&amp;diff=64399</id>
		<title>Построение компонент вершинной двусвязности</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%BE%D1%81%D1%82%D1%80%D0%BE%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BA%D0%BE%D0%BC%D0%BF%D0%BE%D0%BD%D0%B5%D0%BD%D1%82_%D0%B2%D0%B5%D1%80%D1%88%D0%B8%D0%BD%D0%BD%D0%BE%D0%B9_%D0%B4%D0%B2%D1%83%D1%81%D0%B2%D1%8F%D0%B7%D0%BD%D0%BE%D1%81%D1%82%D0%B8&amp;diff=64399"/>
				<updated>2018-03-15T14:45:06Z</updated>
		
		<summary type="html">&lt;p&gt;94.29.126.242: Пофиксил баг /* Псевдокод второго прохода */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Двупроходный алгоритм==&lt;br /&gt;
Найти [[Отношение вершинной двусвязности|компоненты вершинной двусвязности]] неориентированного графа можно с помощью [[Обход_в_глубину,_цвета_вершин |обхода в глубину]].&lt;br /&gt;
&lt;br /&gt;
'''Первый проход:&lt;br /&gt;
[[Использование обхода в глубину для поиска точек сочленения|ищем точки сочленения с помощью обхода в глубину]], заполняем массивы &amp;lt;tex&amp;gt;tin&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;up&amp;lt;/tex&amp;gt;. &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Второй проход:&lt;br /&gt;
[[Точка сочленения, эквивалентные определения|точка сочленения]] принадлежит как минимум двум компонентам вершинной двусвязности.&lt;br /&gt;
Вершина &amp;lt;tex&amp;gt; v \ne root &amp;lt;/tex&amp;gt; является точкой сочленения, если у нее есть сын &amp;lt;tex&amp;gt; u&amp;lt;/tex&amp;gt;, такой что &amp;lt;tex&amp;gt; up[u] \geqslant tin[v] &amp;lt;/tex&amp;gt;. &amp;lt;br&amp;gt; Это также значит, что ребро &amp;lt;tex&amp;gt; vu &amp;lt;/tex&amp;gt; содержится в другой компоненте вершинной двусвязности, нежели ребро по которому мы пришли в вершину &amp;lt;tex&amp;gt; v &amp;lt;/tex&amp;gt; , используя поиск в глубину. Получается, что перейдя по этому ребру, мы окажемся в другой компоненте вершинной двусвязности. &amp;lt;br&amp;gt;&lt;br /&gt;
Используем это свойство, чтобы окрасить компоненты вершинной двусвязности в различные цвета.&amp;lt;br&amp;gt;&lt;br /&gt;
=== Псевдокод второго прохода ===&lt;br /&gt;
* Во время первого запуска &amp;lt;tex&amp;gt;dfs&amp;lt;/tex&amp;gt; будут заполняться массивы &amp;lt;tex&amp;gt;tin&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;up&amp;lt;/tex&amp;gt;, поэтому при запуске функции &amp;lt;tex&amp;gt;paint&amp;lt;/tex&amp;gt; мы считаем, что они уже посчитаны.&lt;br /&gt;
* &amp;lt;tex&amp;gt;\mathtt{maxColor}&amp;lt;/tex&amp;gt; изначально равен &amp;lt;tex&amp;gt;0&amp;lt;/tex&amp;gt;, что эквивалентно тому, что никакое ребро не окрашено.&lt;br /&gt;
* &amp;lt;tex&amp;gt;\mathtt{color}&amp;lt;/tex&amp;gt; хранит в себе цвет, компоненты, из которой вызвалась функция &amp;lt;tex&amp;gt;\mathrm{paint}&amp;lt;/tex&amp;gt; для текущей вершины.&lt;br /&gt;
* &amp;lt;tex&amp;gt;\mathtt{parent}&amp;lt;/tex&amp;gt; {{---}} это вершина, из которой мы попали в текущую.&lt;br /&gt;
&lt;br /&gt;
 '''function''' paint(&amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt;, color, parent):&lt;br /&gt;
   '''for''' &amp;lt;tex&amp;gt; (v, u) \in E&amp;lt;/tex&amp;gt;:&lt;br /&gt;
     '''if''' &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt; == parent&lt;br /&gt;
       '''continue'''&lt;br /&gt;
     '''if''' '''not''' visited[&amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt;]&lt;br /&gt;
       '''if''' up[&amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt;] &amp;lt;tex&amp;gt;\geqslant&amp;lt;/tex&amp;gt; tin[&amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt;]&lt;br /&gt;
         newColor = ++maxColor&lt;br /&gt;
         col[&amp;lt;tex&amp;gt;vu&amp;lt;/tex&amp;gt;] = newColor&lt;br /&gt;
         paint(&amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt;, newColor, &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt;)&lt;br /&gt;
       '''else'''&lt;br /&gt;
         col[&amp;lt;tex&amp;gt;vu&amp;lt;/tex&amp;gt;] = color&lt;br /&gt;
         paint(&amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt;, color, &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt;)&lt;br /&gt;
     '''else''' '''if''' tin[&amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt;] &amp;lt;tex&amp;gt;&amp;lt;&amp;lt;/tex&amp;gt; tin[&amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt;]&lt;br /&gt;
       col[&amp;lt;tex&amp;gt;vu&amp;lt;/tex&amp;gt;] = color&lt;br /&gt;
&lt;br /&gt;
 '''function''' solve():&lt;br /&gt;
   '''for''' &amp;lt;tex&amp;gt; v \in V&amp;lt;/tex&amp;gt;:&lt;br /&gt;
     dfs(&amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt;)    &lt;br /&gt;
   '''for''' &amp;lt;tex&amp;gt; v \in V&amp;lt;/tex&amp;gt;:&lt;br /&gt;
     '''if''' '''not''' visited[&amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt;]&lt;br /&gt;
       maxColor++&lt;br /&gt;
       paint(&amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt;, maxColor, -1)&lt;br /&gt;
&lt;br /&gt;
Ребра каждой из компонент вершинной двусвязности окажутся окрашенными в свой цвет.&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
В алгоритме выполняется два прохода &amp;lt;tex&amp;gt;dfs&amp;lt;/tex&amp;gt;, каждый из которых работает &amp;lt;tex&amp;gt;O(|V| + |E|)&amp;lt;/tex&amp;gt;. Значит время работы алгоритма &amp;lt;tex&amp;gt;O(|V| + |E|)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Однопроходный алгоритм ==&lt;br /&gt;
Заведем [[Стек|стек]], в который будем записывать все дуги в порядке их обработки. Если обнаружена точка сочленения, дуги очередного блока окажутся в этом стеке, начиная с дуги дерева обхода, которая привела в этот блок, до верхушки стека. &amp;lt;br&amp;gt;&lt;br /&gt;
Таким образом, каждый раз находя компоненту вершинной двусвязности мы сможем покрасить все ребра, содержащиеся в ней, в новый цвет.&lt;br /&gt;
=== Доказательство корректности алгоритма ===&lt;br /&gt;
Предположим, что граф содержит точку сочленения &amp;lt;tex&amp;gt; i' \in V &amp;lt;/tex&amp;gt; , за которой следует один или несколько блоков. Вершины из этих блоков образуют подмножество &amp;lt;tex&amp;gt; V' \subset V &amp;lt;/tex&amp;gt;. В таком случае: &amp;lt;br&amp;gt;&lt;br /&gt;
# Все вершины &amp;lt;tex&amp;gt; V' &amp;lt;/tex&amp;gt; являются потомками &amp;lt;tex&amp;gt; i' &amp;lt;/tex&amp;gt; в дереве обхода;&lt;br /&gt;
# Все вершины &amp;lt;tex&amp;gt; V' &amp;lt;/tex&amp;gt; будут пройдены в течение периода серого состояния &amp;lt;tex&amp;gt; i' &amp;lt;/tex&amp;gt;;&lt;br /&gt;
# В &amp;lt;tex&amp;gt; G &amp;lt;/tex&amp;gt; не может быть обратных дуг из &amp;lt;tex&amp;gt; V' &amp;lt;/tex&amp;gt; в &amp;lt;tex&amp;gt; V \setminus V' &amp;lt;/tex&amp;gt;.&amp;lt;br&amp;gt;&lt;br /&gt;
Значит все дуги &amp;lt;tex&amp;gt; V' &amp;lt;/tex&amp;gt; будут будут добавлены в стек после дуги ведущей из точки сочленения в блок. В стеке в момент обнаружения точки сочленения будут находиться только дуги блока, связанного  с ней, т.к. блоки найденные до него (если таковые имеются) будет уже извлечены из стека и покрашены в свой цвет.&amp;lt;br&amp;gt;&lt;br /&gt;
=== Псевдокод ===&lt;br /&gt;
 '''function''' paint(&amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt;, parent):&lt;br /&gt;
   tin[&amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt;] = up[&amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt;] = time++&lt;br /&gt;
   '''for''' &amp;lt;tex&amp;gt; (v, u) \in E&amp;lt;/tex&amp;gt;:&lt;br /&gt;
     '''if''' &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt; == parent &lt;br /&gt;
       '''continue'''&lt;br /&gt;
     '''if''' '''not''' visited[&amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt;]&lt;br /&gt;
       stack.push(&amp;lt;tex&amp;gt;vu&amp;lt;/tex&amp;gt;)&lt;br /&gt;
       paint(&amp;lt;tex&amp;gt;u, v&amp;lt;/tex&amp;gt;)&lt;br /&gt;
       '''if''' up[&amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt;] &amp;lt;tex&amp;gt;\geqslant&amp;lt;/tex&amp;gt; tin[&amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt;]&lt;br /&gt;
         color = maxColor++&lt;br /&gt;
         '''while''' stack.top() != (&amp;lt;tex&amp;gt;vu&amp;lt;/tex&amp;gt;)&lt;br /&gt;
           colors[stack.top()] = color&lt;br /&gt;
           stack.pop()&lt;br /&gt;
         colors[&amp;lt;tex&amp;gt;vu&amp;lt;/tex&amp;gt;] = color&lt;br /&gt;
         stack.pop()&lt;br /&gt;
       '''if''' up[&amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt;] &amp;lt; up[&amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt;]&lt;br /&gt;
         up[&amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt;] = up[&amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt;]&lt;br /&gt;
     '''else''' '''if''' tin[&amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt;] &amp;lt; tin[&amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt;] &lt;br /&gt;
       stack.push(&amp;lt;tex&amp;gt;vu&amp;lt;/tex&amp;gt;)&lt;br /&gt;
     '''else''' '''if''' up[&amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt;] &amp;gt; tin[&amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt;]&lt;br /&gt;
       up[&amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt;] = up[&amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
 '''function''' solve():&lt;br /&gt;
   '''for''' &amp;lt;tex&amp;gt; v \in V&amp;lt;/tex&amp;gt;:&lt;br /&gt;
     dfs(&amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt;)&lt;br /&gt;
   '''for''' &amp;lt;tex&amp;gt; v \in V&amp;lt;/tex&amp;gt;:&lt;br /&gt;
     '''if''' '''not''' visited[&amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt;]:&lt;br /&gt;
       time = 0&lt;br /&gt;
       maxColor++&lt;br /&gt;
       paint(&amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt;, -1)&lt;br /&gt;
&lt;br /&gt;
Во время алгоритма совершается один проход &amp;lt;tex&amp;gt;dfs&amp;lt;/tex&amp;gt;, который работает за &amp;lt;tex&amp;gt;O(|V| + |E|)&amp;lt;/tex&amp;gt;. Внутри него совершается еще цикл, который суммарно выполняет &amp;lt;tex&amp;gt;O(|E|)&amp;lt;/tex&amp;gt; операций, т.к. каждое ребро может быть добавлено в стек только один раз. Следовательно, общее время работы алгоритма &amp;lt;tex&amp;gt;O(|V| + |E|) + O(|E|) = O(|V| + |E|)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== См. также ==&lt;br /&gt;
* [[Использование обхода в глубину для поиска точек сочленения]]&lt;br /&gt;
* [[Построение компонент реберной двусвязности]]&lt;br /&gt;
&lt;br /&gt;
== Источники информации ==&lt;br /&gt;
* В.А.Кузнецов, А.М.Караваев. &amp;quot;Оптимизация на графах&amp;quot; - Петрозаводск, Издательство ПетрГУ 2007&lt;br /&gt;
* [http://rain.ifmo.ru/cat/view.php/vis/graph-general/biconnected-components-2005 Дискретная математика: Алгоритмы {{---}} Компоненты двусвязности, мосты и точки сочленения]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Алгоритмы и структуры данных]]&lt;br /&gt;
[[Категория: Обход в глубину]]&lt;/div&gt;</summary>
		<author><name>94.29.126.242</name></author>	</entry>

	</feed>