Построение компонент вершинной двусвязности — различия между версиями
Kirillova (обсуждение | вклад) (Новая страница: «==Определение== {{Определение |definition = Компонентой вершинной двусвязности графа <tex>G(V, E)</tex> н…») |
Kirillova (обсуждение | вклад) |
||
| Строка 9: | Строка 9: | ||
Используем первый проход, чтобы [[Использование обхода в глубину для поиска точек сочленения|найти точки сочленения.]] <br> | Используем первый проход, чтобы [[Использование обхода в глубину для поиска точек сочленения|найти точки сочленения.]] <br> | ||
Определим для каждой вершины две величины: <tex> enter [i] </tex> - время входа поиска в глубину в вершину <tex> i </tex>, <tex> return [i] </tex> – минимальное из времен входа вершин, достижимых из <tex> i </tex> по дереву <tex> dfs </tex> и не более, чем одному обратному ребру. Ребро к родителю не является обратным ребром. <br> | Определим для каждой вершины две величины: <tex> enter [i] </tex> - время входа поиска в глубину в вершину <tex> i </tex>, <tex> return [i] </tex> – минимальное из времен входа вершин, достижимых из <tex> i </tex> по дереву <tex> dfs </tex> и не более, чем одному обратному ребру. Ребро к родителю не является обратным ребром. <br> | ||
| − | '''Псевдокод: | + | '''Псевдокод первого прохода: |
void dfs(v, parent) { | void dfs(v, parent) { | ||
enter[v] = return[v] = time++; | enter[v] = return[v] = time++; | ||
| Строка 56: | Строка 56: | ||
для всех v вершин графа: | для всех v вершин графа: | ||
если (!used[v]): | если (!used[v]): | ||
| − | dfs(v, c, -1); | + | dfs(v, max(c), -1); |
} | } | ||
Ребра каждой из компонент вершинной двусвязности окажутся окрашенными в свой цвет. | Ребра каждой из компонент вершинной двусвязности окажутся окрашенными в свой цвет. | ||
| + | |||
| + | ==Однопроходный алгоритм== | ||
| + | Предположим, что граф содержит точку сочленения <tex> i' \in V </tex> , за которой следует один или несколько блоков, содержащих вершины <tex> V' \subset V </tex>. В таком случае: <br> | ||
| + | 1) Все вершины <tex> V' </tex> являются потомками <tex> i' </tex> в дереве обхода; | ||
| + | 2) Все вершины <tex> V' </tex> будут пройдены в течение периода серого состояния <tex> i' </tex>. | ||
| + | При этом в <tex> G </tex> не может быть обратных дуг из <tex> V' </tex> в <tex> V \setminus V' </tex>. Воспользуемся этим. | ||
| + | Заведем стек, в который будем записывать все дуги в порядке их обработки. Если обнаружена точка сочленения, дуги очередного блока окажутся в этом стеке, начиная с дуги дерева обхода, которая привела в этот блок, до верхушки стека. | ||
| + | Псевдокод: | ||
| + | void dfs(v, parent) { | ||
| + | enter[v] = return[v] = time++; | ||
| + | used[v] = true; | ||
| + | для всех вершин u смежных v: | ||
| + | если (u == parent): | ||
| + | переходим к следующей итерации | ||
| + | если (!used[u]): | ||
| + | stack <- (vu); | ||
| + | dfs(u, v); | ||
| + | если (return[u] >= enter[v]): | ||
| + | develop(v); // обработка найденной точки сочленения и удаление дуг очередного блока | ||
| + | если (return[u] < return[v]): | ||
| + | return[v] = return[u]; | ||
| + | иначе: | ||
| + | если (return[v] > enter[u]): | ||
| + | return[v] = return[u]; | ||
| + | } | ||
| + | void start() { | ||
| + | used для всех вершин заполняем false | ||
| + | для всех v вершин графа: | ||
| + | если (!used[v]): | ||
| + | time = 0; | ||
| + | dfs(v, -1); | ||
| + | } | ||
| + | Таким образом, каждый раз находя компоненту вершинной двусвязности мы сможем покрасить все ребра, содержащиеся в ней, в новый цвет. | ||
| + | |||
| + | == См. также == | ||
| + | [[Использование обхода в глубину для поиска точек сочленения]] | ||
| + | [[Построение компонент реберной двусвязности]] | ||
| + | |||
| + | ==Литература== | ||
| + | * В.А.Кузнецов, А.М.Караваев. "Оптимизация на графах" - Петрозаводск, Издательство ПетрГУ 2007 | ||
Версия 12:15, 22 декабря 2010
Содержание
Определение
| Определение: |
| Компонентой вершинной двусвязности графа называется подмножество ребер , такое что любые два ребра из него лежат на вершинно простом цикле. |
Построение компонент вершинной двусвязности будем осуществлять с помощью обхода в глубину.
Двупроходный алгоритм
Первый проход
Используем первый проход, чтобы найти точки сочленения.
Определим для каждой вершины две величины: - время входа поиска в глубину в вершину , – минимальное из времен входа вершин, достижимых из по дереву и не более, чем одному обратному ребру. Ребро к родителю не является обратным ребром.
Псевдокод первого прохода:
void dfs(v, parent) {
enter[v] = return[v] = time++;
used[v] = true;
для всех вершин u смежных v:
если (u == parent):
переходим к следующей итерации
если (used[u]):
return[v] := min(return[v], enter[u]);
иначе:
dfs(u, v);
return[v] := min(return[v], return[u]);
}
void start() {
used для всех вершин заполняем false
для всех v вершин графа:
если (!used[v]):
time = 0;
dfs(v, -1);
}
Точка сочленения принадлежит как минимум двум компонентам вершинной двусвязаности.
Вершина является точкой сочленения, если у нее непосредственный сын .
Это так же значит, что ребро содержится в другой компоненте вершинной двусвязности, нежели ребро, по которому мы пришли в вершину , используя поиск в глубину.
Используем это свойство, чтобы окрасить компоненты вершинной двусвязности в различные цвета.
Псевдокод второго прохода:
void dfs(v, c, parent) {
used[v] = true;
для всех вершин u смежных v:
если (u == parent):
переходим к следующей итерации
если (!used[u]):
если (return[u] >= enter[v]):
с2 = newColor();
col[vu] = c2;
dfs(u, c2, v);
иначе:
col[vu] = c;
dfs(u, c, v);
иначе:
если (enter[u] <= enter[v]):
col[vu] = c;
}
void start() {
c = 0;
used для всех вершин заполняем false;
для всех v вершин графа:
если (!used[v]):
dfs(v, max(c), -1);
}
Ребра каждой из компонент вершинной двусвязности окажутся окрашенными в свой цвет.
Однопроходный алгоритм
Предположим, что граф содержит точку сочленения , за которой следует один или несколько блоков, содержащих вершины . В таком случае:
1) Все вершины являются потомками в дереве обхода; 2) Все вершины будут пройдены в течение периода серого состояния .
При этом в не может быть обратных дуг из в . Воспользуемся этим. Заведем стек, в который будем записывать все дуги в порядке их обработки. Если обнаружена точка сочленения, дуги очередного блока окажутся в этом стеке, начиная с дуги дерева обхода, которая привела в этот блок, до верхушки стека. Псевдокод:
void dfs(v, parent) {
enter[v] = return[v] = time++;
used[v] = true;
для всех вершин u смежных v:
если (u == parent):
переходим к следующей итерации
если (!used[u]):
stack <- (vu);
dfs(u, v);
если (return[u] >= enter[v]):
develop(v); // обработка найденной точки сочленения и удаление дуг очередного блока
если (return[u] < return[v]):
return[v] = return[u];
иначе:
если (return[v] > enter[u]):
return[v] = return[u];
}
void start() {
used для всех вершин заполняем false
для всех v вершин графа:
если (!used[v]):
time = 0;
dfs(v, -1);
}
Таким образом, каждый раз находя компоненту вершинной двусвязности мы сможем покрасить все ребра, содержащиеся в ней, в новый цвет.
См. также
Использование обхода в глубину для поиска точек сочленения Построение компонент реберной двусвязности
Литература
- В.А.Кузнецов, А.М.Караваев. "Оптимизация на графах" - Петрозаводск, Издательство ПетрГУ 2007