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

Материал из Викиконспекты
Перейти к: навигация, поиск
(исправлены мелкие ошибки)
Строка 30: Строка 30:
 
     }
 
     }
  
[[Точка сочленения, эквивалентные определения|Точка сочленения]] принадлежит как минимум двум компонентам вершинной двусвязаности.
+
[[Точка сочленения, эквивалентные определения|Точка сочленения]] принадлежит как минимум двум компонентам вершинной двусвязности.
Вершина <tex> u \ne root </tex> является точкой сочленения, если у нее <tex> \exists </tex> непосредственный сын <tex> v : return[v]\ge enter[u] </tex>. <br> Это так же значит, что ребро <tex> uv </tex> содержится в другой компоненте вершинной двусвязности, нежели ребро, по которому мы пришли в вершину <tex> v </tex> , используя поиск в глубину. <br>
+
Вершина <tex> u \ne root </tex> является точкой сочленения, если у нее <tex> \exists </tex> непосредственный сын <tex> v : return[v] \ge enter[u] </tex>. <br> Это так же значит, что ребро <tex> uv </tex> содержится в другой компоненте вершинной двусвязности, нежели ребро, по которому мы пришли в вершину <tex> v </tex> , используя поиск в глубину. <br>
 
Используем это свойство, чтобы окрасить компоненты вершинной двусвязности в различные цвета.<br>
 
Используем это свойство, чтобы окрасить компоненты вершинной двусвязности в различные цвета.<br>
 
'''Псевдокод второго прохода:
 
'''Псевдокод второго прохода:
Строка 52: Строка 52:
 
     }
 
     }
 
     void start() {
 
     void start() {
        c = 0;
 
 
         used для всех вершин заполняем false;
 
         used для всех вершин заполняем false;
 
         для всех v вершин графа:
 
         для всех v вершин графа:
 
             если (!used[v]):
 
             если (!used[v]):
                 dfs(v, max(c), -1);
+
                 dfs(v, -1, -1);
 
     }
 
     }
 
Ребра каждой из компонент вершинной двусвязности окажутся окрашенными в свой цвет.
 
Ребра каждой из компонент вершинной двусвязности окажутся окрашенными в свой цвет.
Строка 62: Строка 61:
 
==Однопроходный алгоритм==
 
==Однопроходный алгоритм==
 
Предположим, что граф содержит точку сочленения <tex> i' \in V </tex> , за которой следует один или несколько блоков, содержащих вершины <tex> V' \subset V </tex>. В таком случае: <br>
 
Предположим, что граф содержит точку сочленения <tex> i' \in V </tex> , за которой следует один или несколько блоков, содержащих вершины <tex> V' \subset V </tex>. В таком случае: <br>
  1) Все вершины <tex> V' </tex> являются потомками <tex> i' </tex> в дереве обхода;
+
# Все вершины <tex> V' </tex> являются потомками <tex> i' </tex> в дереве обхода;
  2) Все вершины <tex> V' </tex> будут пройдены в течение периода серого состояния <tex> i' </tex>.
+
# Все вершины <tex> V' </tex> будут пройдены в течение периода серого состояния <tex> i' </tex>.
 
При этом в <tex> G </tex> не может быть обратных дуг из <tex> V' </tex> в <tex> V \setminus V' </tex>. Воспользуемся этим.
 
При этом в <tex> G </tex> не может быть обратных дуг из <tex> V' </tex> в <tex> V \setminus V' </tex>. Воспользуемся этим.
 
Заведем стек, в который будем записывать все дуги в порядке их обработки. Если обнаружена точка сочленения, дуги очередного блока окажутся в этом стеке, начиная с дуги дерева обхода, которая привела в этот блок, до верхушки стека.
 
Заведем стек, в который будем записывать все дуги в порядке их обработки. Если обнаружена точка сочленения, дуги очередного блока окажутся в этом стеке, начиная с дуги дерева обхода, которая привела в этот блок, до верхушки стека.
Строка 74: Строка 73:
 
                 переходим к следующей итерации
 
                 переходим к следующей итерации
 
             если (!used[u]):
 
             если (!used[u]):
                 stack <- (vu);
+
                 stack.push(vu);
 
                 dfs(u, v);
 
                 dfs(u, v);
 
                 если (return[u] >= enter[v]):
 
                 если (return[u] >= enter[v]):
                     develop(v); // обработка найденной точки сочленения и удаление дуг очередного блока
+
                     c = newColor()
 +
                    пока (stack.top() <> (vu)):
 +
                        color[stack.top()] = c;
 +
                        stack.pop();
 +
                    color[vu] = c;
 +
                    stack.pop();
 
                 если (return[u] < return[v]):
 
                 если (return[u] < return[v]):
 
                     return[v] = return[u];
 
                     return[v] = return[u];
Строка 94: Строка 98:
  
 
== См. также ==
 
== См. также ==
[[Использование обхода в глубину для поиска точек сочленения]]
+
*[[Использование обхода в глубину для поиска точек сочленения]]
[[Построение компонент реберной двусвязности]]
+
*[[Построение компонент реберной двусвязности]]
  
 
==Литература==
 
==Литература==
 
* В.А.Кузнецов, А.М.Караваев. "Оптимизация на графах" - Петрозаводск, Издательство ПетрГУ 2007
 
* В.А.Кузнецов, А.М.Караваев. "Оптимизация на графах" - Петрозаводск, Издательство ПетрГУ 2007

Версия 01:00, 24 декабря 2010

Определение

Определение:
Компонентой вершинной двусвязности графа [math]G(V, E)[/math] называется подмножество ребер [math] S \subset E [/math], такое что любые два ребра из него лежат на вершинно простом цикле.


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

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

Первый проход

Используем первый проход, чтобы найти точки сочленения.
Определим для каждой вершины две величины: [math] enter [i] [/math] - время входа поиска в глубину в вершину [math] i [/math], [math] return [i] [/math] – минимальное из времен входа вершин, достижимых из [math] i [/math] по дереву [math] dfs [/math] и не более, чем одному обратному ребру. Ребро к родителю не является обратным ребром.
Псевдокод первого прохода:

   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);
   }

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

   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() {
       used для всех вершин заполняем false;
       для всех v вершин графа:
           если (!used[v]):
               dfs(v, -1, -1);
   }

Ребра каждой из компонент вершинной двусвязности окажутся окрашенными в свой цвет.

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

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

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

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

   void dfs(v, parent) {
       enter[v] = return[v] = time++;
       used[v] = true;
       для всех  вершин u смежных v:
           если (u == parent): 
               переходим к следующей итерации
           если (!used[u]):
               stack.push(vu);
               dfs(u, v);
               если (return[u] >= enter[v]):
                   c = newColor()
                   пока (stack.top() <> (vu)):
                       color[stack.top()] = c;
                       stack.pop();
                   color[vu] = c;
                   stack.pop();
               если (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