Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
прога_билеты.docx
Скачиваний:
3
Добавлен:
01.03.2025
Размер:
3 Мб
Скачать

Топологическая сортировка. Подсчет числа путей в орграфе.

Topo sort (топологическая сортировка) - упорядочивание вершин бесконтурного ориентированного графа согласно частичному порядку, заданному ребрами орграфа на множестве его вершин.

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

Условия: имеем ацикличный ориентированный граф. Упорядочиваем согласно частичному порядку. Алгоритм: 1. Делаем DFS и пихаем в стек вершину, выходя из нее

2. Выводим вершины из стека в ответ. Сложность: O(|V| + DFS(G))

Подсчет числа путей в орграфе.

Делаем топологическую сортировку графа за O(E + V) (в случае матрицы смежности сложность будет O(V^2)).

Теперь пусть нам надо найти количество путей из s в t. Обозначим количество путей из вершины s в вершину i, как F(i). Изначально F(s) = 1, остальные равны 0. Теперь перебираем вершины в порядке топологической сортировки и вычисляем F: F(i) = СУММА(F(j)), для всех существующих дуг (j, i). Ответом будет F(t).

Сложность O(V+E), c матрицей смежности O(V^2).

Сильно связные компоненты.

Компонента сильной связности – максимальное множество вершин C графа, такое, что для любого U,V из C следует, что сущ. путь из U в V и из V в U

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

Алгоритм Косарайю - алгоритм поиска компонент сильной связности в ориентированном графе.

Алгоритм (Косарайю):

  1. DFS(G)

  2. Строим транспонированный граф (меняем ребра на обратные)

  3. DFS(GT) в порядке убывания f[u]

  4. Полученные деревья поиска – компоненты сильной связности

Сложность: O(|E| + |V|) для разреженного и O(|V|^2) для плотного графа.

Теорема.

С(u,v) и C(x,y) – 2 сильно-связных компоненты.

Если есть путь из u в x, то нет пути из y в v

Доказательство:

u->x->y

y->v->u.

Противоречие, тогда получается, что u->y и y->u, значит, они должны лежать в одной сильно-связной компоненте ▪

f[U]=max(f[u]) из сск U

Лемма:

C,C’ – сск. Пусть сущ. ребро (u,v), u из C, v из C’.

Тогда f(C)>f(C’)

Доказательство:

  1. Первый случай: d[C]<d[C’]

x из C. Значит сущ. путь x->u->->v->w из C’. C’ – потомок x в дереве поиска, значит f[x]=f[C]>f(C’)

  1. d(C)>d[C’], y из С’

Все вершины в C’ становятся потомками y. Значит f[y]=f[C’]. Значит не сущ. пути из C’ в C и f[C]>f[C’]. ▪

Теорема о корректности Косарайю:

При обследовании вершины (которая в первом DFS была последней), можем попасть в вершины только этой компоненты. Из другой вершины (из C’), можно попасть (из-за предыдущей теоремы) или в C’ или в C(но там вершины уже черные, значит не можем). ▪

Поиск кратчайших путей в графе. Алгоритм Флойда. Алгоритм Дейкстры. Алгоритм Форда-Беллмана.

Вес пути – сумма весов ребер, входящих в путь

Вес кратчайшего пути из u в v (b(u,v)) – минимальный по длине путь из u в v, если он существует. Иначе – бесконечность

Кратчайший путь из u в v – путь из u в v, имеющий вес равный b(u,v) (т.к. может быть несколько кратчайших путей)

Задачи по поиску кр. путей:

  1. P(u,v)? – Решаем, сводясь к 2 пункту (более быстрого алгоритма не существует)

  2. P(u, v[i]) для всех I – Дейсктра, Форд-Беллман

  3. Кратчайшие пути из всех вершин во все – Форд-Уоршелл

Свойства релаксаций:

Лемма P = <V1…Vk> - кратчайший путь из 1 в k. Тогда Pij = <Vj…Vj> - кратчайший путь между I и j.

Док-во: Если Pij – не кратчайший, то заменив в P этот участок более коротким путем – уменьшим вес пути P – противоречие.

Вес цикла может быть > 0, < 0 и равным 0

  1. Если < 0 – то кратчайший путь уйдет по нему – поэтому Дейкстра не работает если есть цикл отрицательного веса (в отличии от Форда-Белммана)

  2. Если > 0 – то он не войдет в кратчайший путь

  3. Если == 0 – не имеет смысла ведь, но чтобы не было проблем:

Определения:

|P1k| <= |V| - 1

d[v] – массив кратчайших путей (текущий кратчайший путь из s)

Pi[v] – вершина-родитель в кратчайшем пути

Граф G(Pi) = (V(Pi), E(Pi))

V(Pi) = {v | Pi[v] != NULL} и s – стартовая вершина

E(Pi) = { (Pi[v], v) | v из V(Pi) \ {s}}

Дерево кратчайших путей – ориент подграф G’, что:

  1. V’ – множество вершин, достижимых из s

  2. S – корень, а G’ - дерево

  3. Для каждой вершины v путь из s в v в G’ – кратчайший пусть из s в v в G

Relax(u,v,w)

{

If (d[v] > d[u] + w(u,v))

Then

d[v] = d[u] + w(u,v)

Pi[v] = u

}

b(s,v) – длина кратчайшего пути из s в v

Утверждения:

  1. d[v] >=b(s,v)

  2. b(s,v) <= b(s,u) + w(u,v)

  3. Если нет пути из s в v – d[v] = b(s,v) = inf

  4. Если есть путь s->u, и u->v, и u->v – кратчайший, а d[u] = b(s,u) до релаксации, то после нее – d[v] = b(s,v)

  5. Есть путь P = <V0,..,Vk>. Релаксация шла в порядке (V0,V1), (V1,V2) и т.д

Тогда d[Vk] = b(s, Vk)

  1. Если v из V и d[v] = b(s,v), то подграф G(Pi) – дерево кратчайших путей с корнем s

Доказательства:

  1. От противного

  2. Вес кратчайшего пути из s в v <= весу любого пути из s в v, в том числе, и проходящего на последнем шаге через u

  3. Очевидно

  4. Из s->u – кратчайший

d[v] <= d[u] + w(u,v) = b(s,u) + w(u,v) = b(s,v) (т.к. s->u->v - кратчайший)

  1. Вытекает из 3-его пункта

  2. Следует из 0-4

Решение задачи P(u, v[i]) для всех i:

Через TopSort

  1. TopSort(G)

  2. Init

  3. Перебор вершин в порядке сортировки:

Для всех v из Adj(u)

Relax(u,v,W)

Сложность: O(|E|*|V| + DFS(G)) – вроде

Дейкстра (Dijkstra)

Вход: взвешенный граф (веса положительны), стартовая вершина Выход: вектор расстояний до стартовой вершины (опционально - вектор предков, чтобы восстановить кратчайший путь) Алгоритм:

Проставляем расстояния: для стартовой 0, для остальных inf

Пусть PQ – очередь с приоритетом. PQ->push(s)

While(!PQ.empty())

u = PQ.ExtractMin();

u->color = Black;

for v из Adj(u)

If(v->color == WHITE)

d[v] = w(u,v) + d[u]

PQ.push(v)

v->color = Grey

else if v->color == Grey

if (d[v] > d[u] + w(u,v))

PQ.decreaseKey(v, d[u] + w(u,v) )

Сложность алгоритма Дейкстры зависит от способа нахождения вершины v, а также способа хранения множества непосещенных вершин и способа обновления меток. Обозначим черезn количество вершин, а через m — количество ребер в графе G.

  • В простейшем случае, когда для поиска вершины с минимальным d[v] просматривается все множество вершин, а для хранения величин d используется массив, время работы алгоритма есть  . Основной цикл выполняется порядка n раз, в каждом из них на нахождение минимума тратится порядка n операций. На циклы по соседям каждой посещаемой вершины тратится количество операций, пропорциональное количеству рёбер m (поскольку каждое ребро встречается в этих циклах ровно дважды и требует константное число операций). Таким образом, общее время работы алгоритма  , но, так как  , оно составляет  .

  • Для разреженных графов (то есть таких, для которых m много меньше n²) непосещенные вершины можно хранить в двоичной куче, а в качестве ключа использовать значения d[i], тогда время удаления вершины из   станет  , при том, что время модификации   возрастет до  . Так как цикл выполняется порядка n раз, а количество релаксаций (смен меток) не больше m, скорость работы такой реализации  .

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

Доказательство корректности Дейкстры:

Инвариант – для вершины, вытащенной из очереди уже нашли кр. путь (d[v] = b(s,v))

Это верно для любой вершины из S (множество вершин, для которых b(s,v) уже найдено – были вытащены из PQ)

  1. S = пустое – для него верно

  2. пусть u из S. От противного: d[u] != b(s,u), т.е. d[u] > b(s,u)

Пусть P – кратчайший путь из s в u

s->x->y->u, x из S, а y – еще не из S. u – первая вершина, для которой условие не выполняется

Утверждение:

d[y] = b(s,y)

d[x] = b(S,x) – верно, т.к. х из S (u – первая вершина с неверным условием)

при добавлении u в S - мы релаксируем (x,y)  d[y] = b(s,y) ЧТД

b(s,y) <= b(s,u) – очевидно

Значит d[y] = b(s,y) <= b(s,u) <= d[u] <= d[y], т.к. u – первая вершина с неверным условием, а мы вытащили u – т.е. ее приоритет ниже чем приоритет y  d[u] <= d[y]

Но тогда противоречие с d[u] > b(s,u). чтд

Алгоритм Белмана-Форда:

Алгоритм поиска кратчайшего пути во взвешенном графе Условие: взвешенный граф, можно с отрицательными весами и циклами (с добавлением дополнительной проверки), стартовая вершина. Алгоритм (без отрицательных циклов):

Для всех вершин проставляем расстояние = inf

Для начальной вершины расстояние = 0

For i = 1 to (V - 1)

Для всех ребер (u ,v)

Если d[v] > d[u] + w(u, v)

D[v] =d[u] + w(u, v)

Возвращаем вектор расстояний

Алгоритм (c отрицательными циклами): Алгоритм Беллмана–Форда позволяет очень просто определить, существует ли в графе G отрицательный цикл, достижимый из вершины s. Достаточно произвести внешнюю итерацию цикла не , a ровно |V| раз. Если при исполнении последней итерации длина кратчайшего пути до какой-либо вершины строго уменьшилась, то в графе есть отрицательный цикл, достижимый из s. На основе этого можно предложить следующую оптимизацию: отслеживать изменения в графе и, как только они закончатся, сделать выход из цикла (дальнейшие итерации будут бессмысленны). Сложность; O(|E|*|V|)

Доказательство корректности:

Утверждение: G – без циклов с отриц весом. Для всех достижимых вершин v из s – d[v] = b(s,v) после алг БФ

Док-во:

Рассмотрим v – достижима из s.

P = <V0,…,Vk> - кратчайший ацикличный путь из s в v.

|P| <=|V| - 1 (ацикличный)

Во время i-ой итерации рассматриваем ребро (Vi-1, Vi).

d[v] = d[Vk] = b(s,Vk) = b(s,v). Если v – недостижима – d[v] = inf Иначе d[v] < inf. Чтд

Утверждение – код - Алгоритм (c отрицательными циклами) корректен:

А) Нет цикла с отриц. весом

d[v] = b(s,v) <= b(s,u) + w(u,v) – нер-во треугольника

Верно для любых u и v  условие про уменьшение кр. расстояния в алгоритме не сработает на |V|-ой итерации

Б) есть цикл с отриц. Весом

С=<V0,..,Vk>. V0=Vk – цикл отриц. Веса

d[Vi] <= d[Vi-1] + w(Vi-1, Vi). Просуммируем его от 1 до k.

Т.к. V0=Vk, то сумма d[Vi-1] = сумме d[Vi]  0 <= сумме w(Vi-1, Vi) < 0 – противоречие  условие в алгоритме – сработает.

Чтд

Флойд-Уоршалл (Floyd-Warshall)

Вход: взвешенный орграф Выход: матрица длин кратчайших путей. Алгоритм: For v = 1 to V

For i = 1 to V

For j = 1 to V

Table[ i ][ j ] = min (Table[ i ][ j ], Table[ i ][ v ] + Table[ v ][ j ]);

Сложность: O(|V|^3)

Док-во корректности:

Ключевая идея алгоритма — разбиение процесса поиска кратчайших путей на фазы.

Перед  -ой фазой ( ) считается, что в матрице расстояний   сохранены длины таких кратчайших путей, которые содержат в качестве внутренних вершин только вершины из множества   (вершины графа мы нумеруем, начиная с единицы).

Иными словами, перед  -ой фазой величина   равна длине кратчайшего пути из вершины   в вершину  , если этому пути разрешается заходить только в вершины с номерами, меньшими   (начало и конец пути не считаются).

Легко убедиться, что чтобы это свойство выполнилось для первой фазы, достаточно в матрицу расстояний   записать матрицу смежности графа:   — стоимости ребра из вершины   в вершину  . При этом, если между какими-то вершинами ребра нет, то записать следует величину "бесконечность"  . Из вершины в саму себя всегда следует записывать величину  , это критично для алгоритма.

Пусть теперь мы находимся на  -ой фазе, и хотим пересчитать матрицу   таким образом, чтобы она соответствовала требованиям уже для  -ой фазы. Зафиксируем какие-то вершины   и  . У нас возникает два принципиально разных случая:

  • Кратчайший путь из вершины   в вершину  , которому разрешено дополнительно проходить через вершины совпадает с кратчайшим путём, которому разрешено проходить через вершины множества  .

В этом случае величина   не изменится при переходе с  -ой на  -ую фазу.

  • "Новый" кратчайший путь стал лучше "старого" пути.

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

Тогда заметим, что если мы разобьём этот "новый" путь вершиной   на две половинки (одна идущая  , а другая —  ), то каждая из этих половинок уже не заходит в вершину  . Но тогда получается, что длина каждой из этих половинок была посчитана ещё на  -ой фазе или ещё раньше, и нам достаточно взять просто сумму  , она и даст длину "нового" кратчайшего пути.

Объединяя эти два случая, получаем, что на  -ой фазе требуется пересчитать длины кратчайших путей между всеми парами вершин   и   следующим образом:

new_d[i][j] = min (d[i][j], d[i][k] + d[k][j]);

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

Последнее замечание, которое следует сделать, — то, что можно не создавать отдельную матрицу   для временной матрицы кратчайших путей на  -ой фазе: все изменения можно делать сразу в матрице  . В самом деле, если мы улучшили (уменьшили) какое-то значение в матрице расстояний, мы не могли ухудшить тем самым длину кратчайшего пути для каких-то других пар вершин, обработанных позднее.

Асимптотика алгоритма, очевидно, составляет  .