Использование обхода в глубину для поиска компонент сильной связности — различия между версиями

Материал из Викиконспекты
Перейти к: навигация, поиск
(Доказательство)
(Алгоритм)
Строка 3: Строка 3:
  
 
==Алгоритм==
 
==Алгоритм==
{{Определение
 
|definition=
 
Дополнением или обратным к графу <tex>G</tex> называется такой граф <tex>H</tex>, имеющий то же множество вершин, что и <tex>G</tex>, но в котором две несовпадающие вершины смежны тогда и только тогда, когда они не смежны в <tex>G</tex>}}
 
 
Данная задачи решается с помощью поиска в глубину в 3 этапа:
 
Данная задачи решается с помощью поиска в глубину в 3 этапа:
#Построить обратный граф
+
#Построить граф <tex>H</tex> с обратными (инвертированными) рёбрами
#Выполнить в обратном графе поиск в глубину и найти <tex>f[u]</tex> - время окончания обработки вершины <tex>u</tex>
+
#Выполнить в <tex>H</tex> поиск в глубину и найти <tex>f[u]</tex> - время окончания обработки вершины <tex>u</tex>
 
#Выполнить поиск глубину в <tex>G</tex>, перебирая вершины  во внешнем цикле в порядке убывания <tex>f[u]</tex>
 
#Выполнить поиск глубину в <tex>G</tex>, перебирая вершины  во внешнем цикле в порядке убывания <tex>f[u]</tex>
 
Полученные на 3-ем этапе деревья поиска в глубину будут являться компонентами сильной связности графа <tex>G</tex>.<br>
 
Полученные на 3-ем этапе деревья поиска в глубину будут являться компонентами сильной связности графа <tex>G</tex>.<br>
Так как компоненты сильной связности исходного и обратного графа совпадают, то первый поиск в глубину для нахождения <tex>f[u]</tex> можно выполнить на графе <tex>G</tex>, а второй - на обратном.
+
Так как компоненты сильной связности <tex>G</tex> и <tex>H</tex> графа совпадают, то первый поиск в глубину для нахождения <tex>f[u]</tex> можно выполнить на графе <tex>G</tex>, а второй - на <tex>H</tex>.
 
===Доказательство===
 
===Доказательство===
 
Рассмотрим пару вершин <tex>s</tex> и <tex>t</tex>.
 
Рассмотрим пару вершин <tex>s</tex> и <tex>t</tex>.
 
Если вершины <tex>s</tex> и <tex>t</tex> взаимно достижимы, то они обязательно будут находиться в одном дереве поиска в глубину, поскольку, когда просматривается первая из них, вторая остаётся непосещённой и достижимой из первой и будет просмотрена, прежде чем завершится рекурсивный вызов из корня.<br>
 
Если вершины <tex>s</tex> и <tex>t</tex> взаимно достижимы, то они обязательно будут находиться в одном дереве поиска в глубину, поскольку, когда просматривается первая из них, вторая остаётся непосещённой и достижимой из первой и будет просмотрена, прежде чем завершится рекурсивный вызов из корня.<br>
Теперь докажем, что если <tex>s</tex> и <tex>t</tex> находятся в одном дереве поиска, то они являются сильно связанными. Пусть <tex>r</tex> - корень этого дерева. Тогда <tex>s</tex> достижима из <tex>r</tex>, из чего следует, что в обратном графе <tex>r</tex> достижима из <tex>s</tex>. Но <tex>r</tex> имеет большее время окончания обработки <tex>f[r]</tex> > <tex>f[s]</tex>, из чего следует что в обратном графе существует путь из <tex>r</tex> в <tex>s</tex>. Если бы его не сущевствовало, то путь из <tex>s</tex> в <tex>r</tex> в обратном графе оставлял бы <tex>s</tex> с большим временем оканчания обработки <tex>f[s]</tex>. Тогда в исходном графе существуют пути как из <tex>s</tex> в <tex>r</tex>, так и из <tex>r</tex> в <tex>s</tex>, т.е. <tex>r</tex> и <tex>s</tex> сильно связаны. Те же рассуждения доказывают, что <tex>t</tex> и <tex>r</tex> сильно связаны, из чего следует что <tex>t</tex> и <tex>s</tex> также сильно связаны.
+
Теперь докажем, что если <tex>s</tex> и <tex>t</tex> находятся в одном дереве поиска, то они являются сильно связанными. Пусть <tex>r</tex> - корень этого дерева. Тогда <tex>s</tex> достижима из <tex>r</tex>, из чего следует, что в инвертированном графе <tex>r</tex> достижима из <tex>s</tex>. Но <tex>r</tex> имеет большее время окончания обработки <tex>f[r]</tex> > <tex>f[s]</tex>, из чего следует что в инвертированном графе существует путь из <tex>r</tex> в <tex>s</tex>. Если бы его не существовало, то путь из <tex>s</tex> в <tex>r</tex> в инвертированном графе оставлял бы <tex>s</tex> с большим временем окончания обработки <tex>f[s]</tex>. Тогда в исходном графе существуют пути как из <tex>s</tex> в <tex>r</tex>, так и из <tex>r</tex> в <tex>s</tex>, т.е. <tex>r</tex> и <tex>s</tex> сильно связаны. Те же рассуждения доказывают, что <tex>t</tex> и <tex>r</tex> сильно связаны, из чего следует что <tex>t</tex> и <tex>s</tex> также сильно связаны.
  
 
==Пример реализации==
 
==Пример реализации==

Версия 22:07, 15 января 2011

Постановка задачи

Дан ориентированный граф [math]G[/math]. Требуется найти в этом графе компоненты сильной связанности.

Алгоритм

Данная задачи решается с помощью поиска в глубину в 3 этапа:

  1. Построить граф [math]H[/math] с обратными (инвертированными) рёбрами
  2. Выполнить в [math]H[/math] поиск в глубину и найти [math]f[u][/math] - время окончания обработки вершины [math]u[/math]
  3. Выполнить поиск глубину в [math]G[/math], перебирая вершины во внешнем цикле в порядке убывания [math]f[u][/math]

Полученные на 3-ем этапе деревья поиска в глубину будут являться компонентами сильной связности графа [math]G[/math].
Так как компоненты сильной связности [math]G[/math] и [math]H[/math] графа совпадают, то первый поиск в глубину для нахождения [math]f[u][/math] можно выполнить на графе [math]G[/math], а второй - на [math]H[/math].

Доказательство

Рассмотрим пару вершин [math]s[/math] и [math]t[/math]. Если вершины [math]s[/math] и [math]t[/math] взаимно достижимы, то они обязательно будут находиться в одном дереве поиска в глубину, поскольку, когда просматривается первая из них, вторая остаётся непосещённой и достижимой из первой и будет просмотрена, прежде чем завершится рекурсивный вызов из корня.
Теперь докажем, что если [math]s[/math] и [math]t[/math] находятся в одном дереве поиска, то они являются сильно связанными. Пусть [math]r[/math] - корень этого дерева. Тогда [math]s[/math] достижима из [math]r[/math], из чего следует, что в инвертированном графе [math]r[/math] достижима из [math]s[/math]. Но [math]r[/math] имеет большее время окончания обработки [math]f[r][/math] > [math]f[s][/math], из чего следует что в инвертированном графе существует путь из [math]r[/math] в [math]s[/math]. Если бы его не существовало, то путь из [math]s[/math] в [math]r[/math] в инвертированном графе оставлял бы [math]s[/math] с большим временем окончания обработки [math]f[s][/math]. Тогда в исходном графе существуют пути как из [math]s[/math] в [math]r[/math], так и из [math]r[/math] в [math]s[/math], т.е. [math]r[/math] и [math]s[/math] сильно связаны. Те же рассуждения доказывают, что [math]t[/math] и [math]r[/math] сильно связаны, из чего следует что [math]t[/math] и [math]s[/math] также сильно связаны.

Пример реализации

   vector<vector<int>> g, g1;                                 //g хранит граф в виде списка смежностей, g1 - обратный
   vector<int> color, ord, component;                         //цвет вершины, список вершин в порядке окончания обработки, номер компоненты, к который относиться вершина
   int col;                                                   //номер текущей компоненты
   
   void dfs(int & v)                                          //первый поиск в глубину, определяющий порядок обхода
   {
       color[v] = 1;
       for (unsigned i = 0; i < g[v].size(); ++i)
       {
           if (color[g[v][i]] == 0)
               dfs(g[v][i]);
       }
       ord.push_back(v);                                     
   }
   
   void dfs2(int & v)                                         //второй поиск в глубину, выявляет компоненты сильной связности в графе 
   {
       component[v] = col;
       for (unsigned i = 0; i < g1[v].size(); ++i)
       {
           if (component[g1[v][i]] == 0)
               dfs2(g1[v][i]);
       }
   }
   
   int main()
   {
       ...                                                    //считываем исходные данные, формируем массивы g и g1
       for (int i = 1; i <= n; ++i)                           //формируем массив ord[]
       {
           if (color[i] == 0)
               dfs(i);
       }
       col = 1;
       for (int i = ord.size(); i > 0; --i)                   //ищем компоненты связности, вызывая вершины в обратном порядке
       {                                                      //от сохранённого в ord[], что соответствует уменьшению времени конца обработки f[]
           if (component[ord[i - 1]] == 0)
               dfs2(ord[i - 1]), col++;
       }
   }

По окончании выполнения алгоритма в [math]component[i][/math] имеем номер компоненты, к которой принадлежит вершина [math]i[/math].

Литература

  • Р.Седжвик. "Фундаментальные алгоритмы на С++. Алгоритмы на графах" - СПб, ДиаСофтЮП, 2002