Использование обхода в глубину для поиска компонент сильной связности — различия между версиями
(→Пример реализации) |
(→Пример реализации) |
||
| Строка 13: | Строка 13: | ||
Теперь докажем, что если ''s'' и ''t'' находятся в одном дереве поиска, то они являются сильно связанными. Пусть ''r'' - корень этого дерева. Тогда ''s'' достижима из ''r'', из чего следует, что в обратном графе ''r'' достижима из ''s''. Но ''r'' имеет большее время окончания обработки f[r] > f[s], из чего следует что в обратном графе существует путь из ''r'' в ''s''. Тогда в исходном графе существуют пути как из ''s'' в ''r'', так и из ''r'' в ''s'', т.е. ''r'' и ''s'' сильно связаны. Те же рассуждения доказывают, что ''t'' и ''r'' сильно связаны, из чего следует что ''t'' и ''s'' также сильно связаны. | Теперь докажем, что если ''s'' и ''t'' находятся в одном дереве поиска, то они являются сильно связанными. Пусть ''r'' - корень этого дерева. Тогда ''s'' достижима из ''r'', из чего следует, что в обратном графе ''r'' достижима из ''s''. Но ''r'' имеет большее время окончания обработки f[r] > f[s], из чего следует что в обратном графе существует путь из ''r'' в ''s''. Тогда в исходном графе существуют пути как из ''s'' в ''r'', так и из ''r'' в ''s'', т.е. ''r'' и ''s'' сильно связаны. Те же рассуждения доказывают, что ''t'' и ''r'' сильно связаны, из чего следует что ''t'' и ''s'' также сильно связаны. | ||
==Пример реализации== | ==Пример реализации== | ||
| − | vector<vector<int>> g, g1; | + | vector<vector<int>> g, g1; //g хранит граф в виде списка смежностей, g1 - обратный |
| − | vector<int> color, ord, component; | + | vector<int> color, ord, component; //цвет вершины, список вершин в порядке окончания обработки, номер компоненты, к который относиться вершина |
| − | int col; | + | int col; //номер текущей компоненты |
| − | + | ||
| − | void dfs(int & v) | + | void dfs(int & v) //первый поиск в глубину, определяющий порядок обхода |
{ | { | ||
color[v] = 1; | color[v] = 1; | ||
| Строка 25: | Строка 25: | ||
dfs(g[v][i]); | dfs(g[v][i]); | ||
} | } | ||
| − | ord.push_back(v); | + | ord.push_back(v); |
} | } | ||
| − | void dfs2(int & v) | + | void dfs2(int & v) //второй поиск в глубину, выявляет компоненты сильной связности в графе |
{ | { | ||
| − | + | component[v] = col; | |
for (unsigned i = 0; i < g1[v].size(); i ++ ) | for (unsigned i = 0; i < g1[v].size(); i ++ ) | ||
{ | { | ||
| − | if ( | + | if (component[g1[v][i]] == 0) |
dfs2(g1[v][i]); | dfs2(g1[v][i]); | ||
} | } | ||
| Строка 41: | Строка 41: | ||
{ | { | ||
... | ... | ||
| − | for (int i = 1; i <= n; ++i) | + | for (int i = 1; i <= n; ++i) //формируем массив ord[] |
{ | { | ||
if (color[i] == 0) | if (color[i] == 0) | ||
| Строка 47: | Строка 47: | ||
} | } | ||
col = 1; | col = 1; | ||
| − | for (int i = ord.size(); i > 0; --i) | + | for (int i = ord.size(); i > 0; --i) //ищем компоненты связности, вызывая вершины в обратном порядке |
| − | { | + | { //от сохранённого в ord[], что соответствует уменьшению времени конца обработки f[] |
| − | if ( | + | if (component[ord[i - 1]] == 0) |
dfs2(ord[i - 1]), col++; | dfs2(ord[i - 1]), col++; | ||
} | } | ||
} | } | ||
| − | + | ||
| + | По окончании выполнения алгоритма в component[i] имеем номер компоненты, к которой принадлежит вершина ''i'' | ||
Версия 21:16, 15 января 2011
Постановка задачи
Дан ориентированный граф G. Требуется найти в этом графе компоненты сильной связанности.
Алгоритм
Данная задачи решается с помощью поиска в глубину в 3 этапа:
- Построить транспонированный граф
- Выполнить в транспонированном графе поиск в глубину и найти f[u] - время окончания обработки вершины u
- Выполнить поиск глубину в G, перебирая вершины во внешнем цикле в порядке убывания f[u]
Полученные на 3-ем этапе деревья поиска в глубину будут являться компонентами сильной связности графа G Так как компоненты сильной связности исходного и транспонированного графа совпадают, то первый поиск в глубину для нахождения f[u] можно выполнить на графе G, а второй - на транспонированном
Доказательство
Рассмотрим пару вершин s и t. Если вершины s и t взаимно достижимы, то они обязательно будут находиться в одном дереве поиска в глубину, поскольку, когда просматривается первая из них, вторая остаётся непосещённой и достижимой из первой и будет просмотрена, прежде чем завершится рекурсивный вызов из корня. Теперь докажем, что если s и t находятся в одном дереве поиска, то они являются сильно связанными. Пусть r - корень этого дерева. Тогда s достижима из r, из чего следует, что в обратном графе r достижима из s. Но r имеет большее время окончания обработки f[r] > f[s], из чего следует что в обратном графе существует путь из r в s. Тогда в исходном графе существуют пути как из s в r, так и из r в s, т.е. r и s сильно связаны. Те же рассуждения доказывают, что t и r сильно связаны, из чего следует что t и s также сильно связаны.
Пример реализации
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()
{
...
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++;
}
}
По окончании выполнения алгоритма в component[i] имеем номер компоненты, к которой принадлежит вершина i