![]() Главная страница Случайная страница КАТЕГОРИИ: АвтомобилиАстрономияБиологияГеографияДом и садДругие языкиДругоеИнформатикаИсторияКультураЛитератураЛогикаМатематикаМедицинаМеталлургияМеханикаОбразованиеОхрана трудаПедагогикаПолитикаПравоПсихологияРелигияРиторикаСоциологияСпортСтроительствоТехнологияТуризмФизикаФилософияФинансыХимияЧерчениеЭкологияЭкономикаЭлектроника |
Достижимость в графе DAG
Мы завершим наше изучение графов DAG рассмотрением задачи вычисления транзитивного замыкания DAG. Можно ли разработать такие алгоритмы для графов DAG, которые обладают большей эффективностью, чем алгоритмы для обобщенных орграфов, которые исследовались в разделе 19.3? Любой метод топологической сортировки может служить основой алгоритмов транзитивного замыкания DAG, например, мы осуществляем обход вершин в обратном топологическом порядке, вычисляя при этом вектор достижимости для каждой вершины (он является строкой матрицы транзитивного замыкания) из строк, соответствующих смежным с ней вершинам. Обратная топологическая сортировка гарантирует, что все эти строки будут вычислены заблаговременно. В итоге мы проверяем каждый из V элементов вектора, соответствующего вершине назначения каждого их Е ребер, выполняется ли его обработка за время, пропорциональное EV. И хотя этот метод достаточно прост для программной реализации, по эффективности обработки графов DAG он ничуть не лучше, чем для обработки орграфов общего типа. Когда мы используем стандартный поиск в глубину для топологической сортировки (см. программу 19.7), мы можем повысить ее производительность для некоторых видов графов DAG, как показывает программа 19.9. Поскольку в графах DAG нет циклов, то ни в каких поисках в глубину нет обратных ребер. Что еще важнее, как поперечные, так и прямые ребра ведут в узлы, в которых DFS завершен. Чтобы воспользоваться этим обстоятельством, мы разработаем рекурсивную функцию, вычисляющую все вершины, достижимые из заданной исходной вершины, тем не менее (как это характерно для поиска в глубину), мы не производим рекурсивных вызовов для вершин, для которых множество достижимых вершин уже было вычислено. В этом случае достижимые верши- Часть 5. Алгоритмы на графах
Использование этой версии поиска в глубину можно охарактеризовать как применение динамического программирования для вычисления транзитивного замыкания, поскольку мы используем результаты, которые уже были вычислены (и сохранены в строках матриц смежности, соответствующих вершинам, которые обрабатывались раньше) с тем, чтобы не совершать ненужных рекурсивных вызовов. Рисунок 19.27 служит иллюстрацией вычисления транзитивного замыкания графа DAG, представленного в качестве примера на рис. 19.6. Свойство 19.13. С помощью динамического программирования и поиска в глубину мы можем обеспечить постоянное время ответа на запрос на абстрактное транзитивное замыкание графа DAG, при этом на предварительную обработку (для подготовки вычисления транзитивного замыкания) затрачивается пространство памяти, пропорциональное V2, и время, пропорциональное V2 + VX, где X — это число поперечных ребер в лесе DFS. Доказательство: Доказательство непосредственно следует по индукции из рекурсивной функции, используемой в программе 19.9. Мы посещаем вершины в обратном топологическом порядке. Каждое ребро указывает на вершину, для которой мы уже вычислили множество всех достижимых вершин, в силу этого обстоятельства мы можем вычислить множество достижимых вершин для любой вершины путем слияния множеств достижимых вершин, ассоциированных с вершинами назначения каждого ребра. Завершает это слияние РИСУНОК 19.27. ТРАНЗИТИВНОЕ ЗАМЫКАНИЕ ГРАФА DAG Рассматриваемая последовательность векторов-строк есть транзитивное замыкание графа DAG, показанного на рис. 19.21. Эти строки были построены во время выполнения обратной топологической сортировки, вычисленной как завершающее действие рекурсивной функции DFS (см. программу 19.9). Каждая строка является результатом логической операции or (или) над строками смежных вершин, которые заранее появились в рассматриваемом списке в результате предшествовавших вычислений. Например, чтобы вычислить строку для вершины 0, мы выполняем логическую or над строками для вершин 5, 2, 1 и б (и подстановку 1 вместо самой вершины 0), поскольку ребра 0-5, 0-2, 0-1 и 0-6 приводят нас из вершины 0 в любую вершину, которая достижима их каждой из этих вершин. Мы можем игнорировать прямые ребра, поскольку они не добавляют новой информации. Например, мы игнорируем ребро, ведущее из 0 в 3, поскольку вершины, достижимые из 3 уже фигурируют в строке, соответствующей 2. Глава 19. Орграфы и ориентированные ациклические графы
Программа 19.9. Транзитивное замыкание графа DAG_____________________________ Конструктор в этом классе вычисляет транзитивное замыкание DAG при помощи одного поиска в глубину. Он вычисляет в рекурсивном режиме вершины, достижимые из каждой вершины из числа достижимых вершин ее потомков в дереве DFS. template < class tcDag, class Dag> class dagTC { tcDag T; const Dag & D; int cnt; vector< int> pre; void tcR(int w) { pre[w] = cnt++; typename Dag:: adjIterator A(D, w); for (int t = A.beg();! A.end(); t = A.nxt()) {
|