Существует ли правильный алгоритм для решения проблемы удаления ребер?

Имеется ориентированный граф (не обязательно связный), один или несколько узлов которого выделены как источники. Любой узел, доступный из любого из источников, считается «освещенным». Теперь предположим, что одно из ребер удалено. Проблема состоит в том, чтобы определить узлы, которые ранее горели и больше не горят.

Полагаю, аналогия с городской системой электроснабжения может быть рассмотрена.


person tafa    schedule 05.01.2009    source источник
comment
Я предполагаю, что граф не должен быть связан (т.е. допустимы леса)?   -  person Triptych    schedule 05.01.2009
comment
Вы правы, это не обязательно.   -  person tafa    schedule 05.01.2009
comment
Могут ли быть циклы в графе?   -  person The Archetypal Paul    schedule 05.01.2009
comment
И добавляются ли когда-либо узлы/ребра, или вы начинаете с графа, а затем удаляете только ребра?   -  person The Archetypal Paul    schedule 05.01.2009
comment
И (просто стало любопытно) это проблема, возникающая из-за написания какого-то генератора кода?   -  person The Archetypal Paul    schedule 05.01.2009
comment
Павел, У вас могут быть циклы в графе. Никаких новых узлов или ребер не добавляется. И это не имеет никакого отношения к генераторам кода.   -  person tafa    schedule 05.01.2009


Ответы (5)


Это проблема "достижимости динамического графа". Следующая статья должна быть полезной:

Полностью динамический алгоритм достижимости ориентированных графов с почти линейное время обновления. Лиам Родитти, Ури Цвик. Теория вычислений, 2002 г.

Это дает алгоритм с обновлениями времени O(m * sqrt(n)) (амортизированный) и запросами времени O(sqrt(n)) на возможно циклическом графе (где m — число ребер и n количество узлов). Если граф является ациклическим, его можно улучшить до обновлений за O(m) (амортизированных) и запросов за O(n/log n).

Всегда возможно, что вы могли бы сделать лучше, учитывая конкретную структуру вашей проблемы или обменяв пространство на время.

person Chris Conway    schedule 05.01.2009

Если вместо просто «освещенных» или «неосвещенных» вы сохранили бы набор узлов, от которых узел запитывается или освещается, и рассматривали узел с пустым набором как «неосвещенный», а узел с непустым набором — как « горит», то удаление ребра будет означать просто удаление исходного узла из набора целевых узлов.

РЕДАКТИРОВАТЬ: Забыл это: и если вы удалите последний освещенный узел в наборе, пройдите по краям и удалите узел, который вы только что «не зажгли» из их набора (и, возможно, тоже пересечете его и т. д.)

РЕДАКТИРОВАТЬ2 (перефразировать для тафа): Во-первых: я неправильно понял исходный вопрос и подумал, что в нем говорилось, что для каждого узла уже известно, горит он или нет, что, как я перечитал сейчас, не упоминалось.

Однако, если для каждого узла в вашей сети вы храните набор, содержащий узлы, через которые он был освещен, вы можете легко пройти по графу от удаленного ребра и исправить любые освещенные/неосвещенные ссылки. Так, например, если у нас есть узлы A, B, C, D, как это: (хромая попытка ascii-арта)

A -> B >- D
 \-> C >-/

Затем в узле A вы сохраните, что это был источник (и, следовательно, освещенный сам по себе), и в B, и в C вы сохраните, что они были освещены A, а в D вы сохраните, что он был освещен как A, так и C. .

Затем скажем, что мы удаляем ребро из B в D: В D мы удаляем B из списка освещенных источников, но он остается освещенным, поскольку он все еще освещается A. Далее, скажем, мы удаляем ребро из A в C после этого: A удаляется из набора C, и, таким образом, C больше не горит. Затем мы переходим по ребрам, исходящим из C, и удаляем C из набора D, который теперь также не освещен. В этом случае мы закончили, но если бы набор был больше, мы бы просто продолжили с D.

Этот алгоритм будет когда-либо посещать только те узлы, на которые непосредственно влияет удаление или добавление ребра, и поэтому (за исключением дополнительного хранилища, необходимого на каждом узле) он должен быть близок к оптимальному.

person Frans-Willem    schedule 05.01.2009
comment
Я ужасно извиняюсь, но я не мог следовать вашему предложению, хотя я прочитал его 3-4 раза. Не могли бы вы перефразировать это? - person tafa; 05.01.2009
comment
Искусство Ascii прекрасно, а ваш ответ еще лучше, он принес другую перспективу. Спасибо. - person tafa; 05.01.2009

Это твоя домашняя работа?

Самое простое решение — сделать DFS (http://en.wikipedia.org/wiki/Depth-first_search) или BFS (http://en.wikipedia.org/wiki/Breadth-first_search) на исходном графе, начиная с исходных узлов. Это даст вам все оригинальные освещенные узлы.

Теперь удалите рассматриваемое ребро. Сделайте еще раз ДФС. Вы можете узлы, которые все еще остаются освещенными.

Выведите узлы, которые входят в первый набор, но не входят во второй.

Это асимптотически оптимальный алгоритм, поскольку вы выполняете две DFS (или BFS), которые занимают O (n + m) раз и пространство (где n = количество узлов, m = количество ребер), которые преобладают по сложности. Вам нужно как минимум o(n + m) времени и места, чтобы прочитать ввод, поэтому алгоритм оптимален.

Теперь, если вы хотите удалить несколько ребер, это было бы интересно. В этом случае мы будем говорить о динамических структурах данных. Это то, что вы намеревались?

РЕДАКТИРОВАТЬ: Принимая во внимание комментарии:

  • не подключен не проблема, так как узлы в недостижимых связных компонентах не будут достигнуты при поиске
  • есть умный способ сделать DFS или BFS сразу со всех нод (я опишу BFS). Вам просто нужно поместить их все в начале в стек/очередь.

Псевдокод для BFS, который ищет все узлы, достижимые из любого из начальных узлов:

Queue q = [all starting nodes]
while (q not empty)
{
   x = q.pop()
   forall (y neighbour of x) {
      if (y was not visited) {
         visited[y] = true
         q.push(y)
      }
   }
}

Замените очередь стеком, и вы получите что-то вроде DFS.

person user51568    schedule 05.01.2009
comment
DFS/BFS предполагает связный граф. В этой задаче не так. - person Triptych; 05.01.2009
comment
Отключено не проблема. Выполнить DFS с каждого исходного узла? - person The Archetypal Paul; 05.01.2009
comment
Да, DFS из каждого источника работает, но он такой дрянной, что я не стал его публиковать. Я чувствую, что есть что-то лучше. - person Triptych; 05.01.2009
comment
Не знаю, почему это было отмечено. Запустите модифицированную DFS на каждом исходном узле. Отметьте каждый узел, в который вы попали, как зажженный. Игнорируйте любые узлы, которые вы встречаете, которые уже освещены, чтобы передать циклы на графике. - person SmacL; 05.01.2009
comment
Правильно, но вам нужно сделать это только один раз, а затем вы можете повторить результат. Хм. Если вы отбрасываете узлы, которые изначально не освещены, вы спрашиваете, является ли этот набор ребер разрезом? / — граф несвязный. Не уверен, что это поможет, хотя - person The Archetypal Paul; 05.01.2009
comment
Это то же самое чувство, которое заставило меня опубликовать сообщение, поскольку я уже рассмотрел это решение, прежде чем спрашивать. Но все равно спасибо. - person tafa; 05.01.2009
comment
Я изменил ответ, чтобы лучше объяснить, как вычислить достижимый набор узлов за один шаг. - person user51568; 05.01.2009

Насколько велики и насколько связаны графы? Вы можете сохранить все пути от исходных узлов ко всем другим узлам и искать узлы, все пути к этому узлу содержат одно из удаляемых ребер.

РЕДАКТИРОВАТЬ: немного расширить это описание

Выполните DFS с каждого исходного узла. Отслеживайте все пути, сгенерированные к каждому узлу (как ребра, а не вершины, поэтому нам нужно знать только задействованные ребра, а не их порядок, и поэтому мы можем использовать растровое изображение). Ведите подсчет для каждого узла количества путей от источника к узлу.

Теперь перебираем пути. Удалите все пути, содержащие удаленные ребра, и уменьшите значение счетчика для этого узла. Если счетчик узла уменьшился до нуля, он был освещен, а теперь нет.

person The Archetypal Paul    schedule 05.01.2009
comment
Извините, забыл упомянуть, что размер проблемы варьируется, хотя я могу сказать, что отзывчивость системы важна; планируется, что это будет веб-сервис. - person tafa; 05.01.2009

Я бы сохранил информацию о подключенных исходных узлах на ребрах при построении графа (например, если ребро имеет подключение к источникам S1 и S2, его список источников содержит S1 и S2). И создайте узлы с информацией о входных ребрах и выходные края. Когда ребро удалено, обновите выходные ребра целевого узла этого ребра, учитывая входные ребра узла. И пройдите через все целевые узлы обновленных ребер, используя DFS или BFS. (В случае циклического графа рассмотрите возможность маркировки). При обновлении графа также можно найти узлы без какого-либо ребра, имеющего соединение с источником (освещенные->неосвещенные узлы). Однако это может быть не очень хорошим решением, если вы хотите удалить несколько ребер одновременно, поскольку это может привести к повторному обходу одних и тех же ребер.

person hakan    schedule 05.01.2009