1
ГРАФЫ
2
8.3. Алгоритм Флойда - Уоршалла
Транзитивное замыкание графа отношения.
Ориентированный граф – это отношение. Это отношение транзитивно, если
∀u, v, w: u R v, v R w ⇒ u R w
Транзитивное замыкание отношения – пополнение отношения новыми парами так,
чтобы пополненное отношение стало транзитивным (необходимо добавить
минимальное число таких пар).
1
3
6
4
2
7
5
Отношение не транзитивноОтношение транзитивно
Задача нахождения транзитивного замыкания на языке графов:
провести новую дугу из u в v, если в исходном графе существовал путь из u в v.
3
Транзитивное замыкание графа отношения. Алгоритм «умножения матриц».
1
3
6
4
2
7
5
0 0 0 0 0 1 0
0 0 0 0 1 0 1
1 0 0 0 0 0 0
0 1 0 0 0 0 1
0 0 0 0 0 0 0
0 0 1 0 0 0 0
0 0 0 0 1 0 0
2
1
4
3
6
5
7
1 2 3 4 5 6 7
Пусть матрица G(l)
представляет собой
граф путей длиной l (то есть в матрице
единица находится в ячейке (u,v), если
в исходном графе существовал путь из
u в v длиной не больше l ).
Тогда матрица G(1)
– это матрица смежности исходного графа G,
G(n)
– матрица смежности его транзитивного замыкания (очевидно, что если в графе
существует путь длины, большей n, то существует и путь, длины не большей n).
Алгоритм нахождения транзитивного замыкания: если удается вычислить G(l+1)
по G(l)
,
то можно, начав с матрицы G, за n шагов получить матрицу G(n)
.
4
Алгоритм «умножения матриц».
Пусть матрица G(l)
представляет собой граф путей длиной l (то есть в матрице G(l)
единица находится в ячейке (u,v), если в исходном графе существовал путь из
u в v длиной не больше l ). Тогда что такое матрица G(l+1)
?
u v
l +1
w
l
G(l+1)
[u,v] = 1, если найдется w такое, что
либо G(l)
[u,v] = 1,
либо G(l)
[u,w] = 1 и G [w,v] = 1.
G(l+1)
[u,v] = G(l)
[u,v] + G(l)
[u,w] × G [w,v]∑=
n
w 1
(умножение и сложение понимаются в смысле логических операций «и» и «или»)
В матричном виде: G(l+1)
= G(l)
+ G(l)
× G
На самом деле количество операций можно сократить, если заметить, что длины
путей можно каждый раз увеличивать вдвое: G(2l)
= G(l)
+ G(l)
× G(l)
то есть «в матрице существует путь между вершинами u и v длиной не более 2l,
если уже существовал путь длиной не более l или если найдется такая
вершина w, что был путь от u до w и от w до v, оба длиной не больше l.
5Глава 8. Графы.
Алгоритм «умножения матриц».
public static boolean[][] multiply (boolean[][] matr1, boolean[][] matr2) {
int n = matr1.length;
boolean[][] matr = new boolean[n][n];
for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) {
matr[i][j] = false;
for (int k = 0; k < n; k++) {
matr[i][j] ||= matr1[i][k] && matr2[k][j];
}}}
return matr;
}
public static boolean[][] add (boolean[][] matr1, boolean[][] matr2) {
int n = matr1.length;
boolean [][] matr = new boolean[n][n];
for (int i = 0; i < n; ++i)
for (int j = 0; j < n; ++j)
matr[i][j] = matr1[i][j] + matr2[i][j];
}
public static boolean[][] transClosure(boolean[][] G) {
boolean[][] Gl = G;
for (int l = 1; l < n; l*=2)
Gl = add(Gl, multiply(Gl, Gl));
}
6
Алгоритм Флойда – Уоршалла
Алгоритм умножения матриц требует порядка n3
log n простых логических операций.
Алгоритм Флойда – Уоршалла требует лишь n3
простых операций и не требует
дополнительной памяти под промежуточные матрицы.
Пусть матрица G(l)
представляет собой граф путей, проходящих через
промежуточные вершины с номерами от 0 до l -1 (то есть в матрице G(l)
единица
находится в ячейке (u,v), если в исходном графе существовал путь из u в v,
проходящий только через вершины из множества {0,… l -1}, u и v в это множество
не входят). Тогда что такое матрица G(l+1)
?
u v
{0,1,…l }
u v
{0,1,…l -1}
1) G(l+1)
[u,v] = 1, если G(l)
[u,v] == 1
u v
{0,1,…l -1}
2) G(l+1)
[u,v] = 1, если G(l)
[u,l] == 1 && G(l)
[l,v] == 1l
{0,1,…l -1}
G(l+1)
[u,v] = G(l)
[u,v] || G(l)
[u,l] && G(l)
[l,v]
7
Алгоритм Флойда – Уоршалла нахождения транзитивного замыкания
G(
l+1)
[u,v] = G(
l)
[u,v] || G(
l)
[u,l] && G(
l)
[l,v]



==
==
=+
1],[,],[],[
0],[,],[
],[ )()()(
)()(
)1(
luGеслиvlGvuG
luGеслиvuG
vuG lll
ll
l
||



==
==
=+
1],[,][][
0],[,][
][ )()()(
)()(
)1(
luGеслиlGuG
luGеслиuG
uG lll
ll
l
||
При u = l всегда G(l+1)
[u] = G(l)
[u]
public static boolean[][] transClosure (boolean[][] G) {
int n = G.length;
for (int l = 0; l < n; l++) {
// Формирование матрицы G(l+1):
for (int u = 0; u < n; u++) {
if (G[u][l]) {
for (int v = 0; v < n; v++) {
G[u][v] ||= G[l][v];
}
}
}
}
return G;
}
8Глава 8. Графы.
Применение алгоритма Флойда – Уоршалла для поиска кратчайших путей
Пусть матрица G(l)
представляет собой граф кратчайших путей, проходящих через
промежуточные вершины с номерами от 0 до l -1. То есть в матрице G(l)
в ячейке (u,v)
находится длина кратчайшего пути из u в v, если он существовал, проходящий
только через вершины из множества {0,… l -1}, u и v в это множество не входят.
Если пути из u в v не было, то в соответствующей ячейке матрицы будет значение ∞.
G(l+1)
[u,v] = min(G(l )
[u,l] + G(l )
[l,v], G(l )
[u,v])u v
{0,1,…l -1}
l
{0,1,…l -1}
{0,1,…l -1}
public static double[][] minPathsMatrix (double[][] G) {
int n = G.length;
for (int l = 0; l < n; l++) {
// Формирование матрицы G(l+1):
for (int u = 0; u < n; u++) {
if (G[u][l] < Double.MAX_VALUE) {
for (int v = 0; v < n; v++) {
if (G[l][v] < Double.MAX_VALUE)
G[u][v] = Math.min(G[u][l] + G[l][v] , G[u][v]);
}}}}
return G;
}
9
Применение алгоритма Флойда – Уоршалла для поиска кратчайших путей
Помимо длин путей необходимо найти еще и матрицу направлений (аналог дерева
предшествования для случая поиска путей из одной вершины).
P [u,v] = p, где p – первая вершина на кратчайшем пути из u в v.
P(0)
[u,v] = v, если G [u,v] < ∞ и не определено, если G [u,v] = ∞.
P(l +1)
[u,v] = P(l )
[u,v], если не было коррекции кратчайшего пути.
P(l +1)
[u,v] = P(l )
[u,l ], если была коррекция кратчайшего пути.
public static int[][] minPathsMatrix (double[][] G) {
int n = G.length; int P[][] = new int[n][n];
for (int i = 0; i < n; i++) for (int j = 0; j < n; j++)
if (G[i][j] < Double.MAX_VALUE) P[i][j] = j;
for (int l = 0; l < n; l++) {
for (int u = 0; u < n; u++) {
if (G[u][l] < Double.MAX_VALUE) {
for (int v = 0; v < n; v++) {
if (G[l][v] < Double.MAX_VALUE) {
if (G[u][l] + G[l][v] < G[u][v]) {
G[u][v] = G[u][l] + G[l][v]; P[u][v] = P[u][l];
} } } } } }
return P;
}
10Глава 8. Графы.
Алгоритм Джонсона для разреженных графов
Покажем, что задачу о поиске минимальных путей в произвольном графе
без циклов с отрицательным весом можно свести к задаче о поиске путей
в графе с неотрицательными весами ребер.
Выберем произвольную функцию h(u), которая приписывает вещественные
числа всем вершинам графа. Заменим вес каждого ребра
w(u,v) на w1(u,v) = w(u,v) + h(u) – h(v)
Тогда общий вес любого пути из s в f изменится на постоянную величину
h(s) – h(f)
Осталось подобрать величины h(u) таким образом, чтобы веса всех ребер стали
неотрицательными.
Если в графе веса всех ребер неотрицательны, то для нахождения всех кратчайших
путей между всеми парами вершин можно n раз применить алгоритм Дейкстры.
Время работы такого алгоритма составляет порядка n (m + n) log n. Если ребер
в графе немного (порядка n), то это время может оказаться лучше, чем для
алгоритма Флойда – Уоршалла, который занимает время порядка n3
.
11
Алгоритм Джонсона для разреженных графов
0
0
-1
0
-3-4
-3 -2
-1
2
2
3 -3
2
-1
3
2
2
-1
2
4
-1
2
1
-2
3
2
2
1. Проведем дополнительные
дуги нулевой длины из
новой вершины во все
вершины графа.
2. Вычислим длины
минимальных путей из
новой вершины во все
остальные (например, с
помощью алгоритма
Беллмана – Форда).
3. Положим h(u) равными
вычисленным длинам
путей.
Модифицированные длины ребер показаны на рисунке красным цветом.
5
00
3
0
0
2
1
0
0
2
6
2
0
1
0 5
3
2
Алгоритм Беллмана – Форда имеет время работы порядка mn, поэтому общее
время нахождения всех кратчайших путей будет порядка mn log n.
12Глава 8. Графы.
Построение минимального скелета нагруженного графа. Алгоритм Прима.
10
2
3
6
1
85
7
4
9
1
1
1
2
2
2
3
4
3
3
4
4
5
5
5
Запускаем алгоритм обхода графа, начиная с произвольной вершины.
В качестве контейнера выбираем очередь с приоритетами. Приоритет – текущая
величина найденного расстояния до уже построенной части опорного дерева.
Релаксации (как в Алгоритме Дейкстры) подвергаются прямые и обратные ребра.
1 2 3 4 5 6 7 8 9 10n
π
0 ∞ ∞ ∞ ∞ ∞ ∞ ∞ ∞ ∞d 1 3
1 1 2
2
2 2 2
25 44
5
3
10
1
3
4
6
3
4
1
9
2
8
Оценка времени работы алгоритма –
O((m + n) log n) – коррекция очереди
с приоритетами происходит при
присоединении каждой новой вершины
и при анализе каждого нового ребра.
В результате работы получили список
ребер опорного дерева (скелета)
вместе с нагрузками на все ребра.
13
Построение минимального скелета нагруженного графа. Алгоритм Крускала.
10
2
3
6
1
85
7
4
9
1
3
5
2
2
4
1
4
3
3
2
4
5
5
1
1. Строим список ребер,
упорядоченный по
возрастанию нагрузки
1-2
3-6
8-9
2-5
2-10
8-7
1-4
4-9
3-10
2-8
5-6
6-7
2-3
6-10
4-8
1
1
1
2
2
2
3
3
3
4
4
4
5
5
5
2. Строим лес из изолированных вершин
3. Добавляем ребра по порядку, следя за тем,
чтобы концы ребер лежали в разных деревьях
1 2 3 4 5 6 7 8 9 10
0
0
0
0
0
0
0
0
0
0
1
2
3
4
5
6
7
8
9
10
2
4
4
4
7
7
3
7
7
14
Сети и потоки
s
1 2
3
t
4
Сеть – это ориентированный нагруженный граф, в котором нагрузка имеет
интерпретацию «пропускная способность» (разумеется, положительная; можно
считать, что отсутствующая дуга соответствует нулевой нагрузке). Будем обозначать
эту нагрузку c (u, v).
16
10
13
4
12
9
14
20
7
4
Мы будем считать, что
a) в сети есть две выделенные вершины – исток (только исходящие дуги) и сток
(только входящие дуги);
b) любая вершина лежит на каком-нибудь пути из истока в сток (нет «бесполезных»
вершин).
15Глава 8. Графы.
Сети и потоки
s
1 2
3
t
4
16
10
13
4
12
9
14
20
7
4
Поток в сети – это задание некоторой дополнительной нагрузки на дуги, обладающей
свойствами:
a) поток по дуге не может превышать пропускной способности дуги и всегда
неотрицателен;
b) в любую вершину (кроме истока и стока) количество втекающей жидкости равно
количеству вытекающей.
11/16
12/12
1/4
8/13
4/9
11/14
15/20
7/7
4/4
Интерпретация потока: количество жидкости, которое в действительности проходит
по заданным каналам (дугам).
16Глава 8. Графы.
Формальное определение потока
s
1 2
3
t
4
Пусть задана сеть. Зададим функцию f (u, v), обладающую следующими свойствами:
10
b) f (u, v) = - f (v, u) – поток – это кососимметричная функция
c) для любой вершины u кроме истока и стока
На приведенной выше картинке: f (S, 3) = 8; f (3, 2) = -4; f (1, 3) = -1.
Для каждой дуги (при заданном потоке f) определим также ее «остаточную пропускную
способность»: cf(u, v) = c (u, v) – f(u, v)
Например, для заданного потока cf(3, 4) = 3, cf(1, 3) = 11.
11/16
12/12
1/4
8/13
4/9
11/14
15/20
7/7
4/4
a) f (u, v) ≤ c(v, u) – поток не превышает пропускной способности дуги
0),( =∑∈Vv
vuf
Функция потока f имеет смысл: количество вещества, перемещающегося из вершины u
в вершину v. Из свойств (a) и (b) следует, что если вершины u и v не соединены дугой,
то f(u, v) = 0. Из этого следует, что в формуле (с) можно ограничиться инцидентными
дугами.
17Глава 8. Графы.
Лемма о потоке в сети
Обозначение: будем писать f (X, Y), где X и Y – множества вершин, подразумевая
под этим ∑∑∈ ∈Xx Yy
yxf ),( На самом деле в эту сумму входят только значения
потока для тех вершин x и y, для которых имеется дуга, ведущая из x в y.
Тогда для любого потока f справедливы следующие соотношения:
f (X, X) = 0 для любого X ⊂ V
f (X, Y) = – f (Y, X) для любых X, Y ⊂ V
f (X ∪ Y, Z) = f (X, Z) + f (Y, Z) для любых X, Y, Z ⊂ V при X ∩ Y = ∅
Назовем величиной потока | f | = f (s, V)
Покажем, что | f | = f (V, t)
f (s, V) = f (V, V) – f (V  s, V) = f (V, V  s) = f (V, t) + f (V, V  s  t) = f (V, t)
Разрез в сети – это разбиение множества вершин V на два подмножества S и T
такие, что s ∈ S, t ∈T. Очевидно, что поток через любой разрез f (S, T) = | f |.
Действительно, f (S, T) = f (S, V) – f (S, S) = f (S, V) = f (s, V) + f (S  s, V) = f (s, V) = | f |
Например, закон сохранения потока можно коротко записать так: f (u, V) = 0 для
всех u ∈ V – { s, t }
18Глава 8. Графы.
Остаточная сеть
Для заданного потока f (u, v) в сети с пропускной способностью ребер c (u, v)
построим остаточную сеть r (u, v) = c (u, v) – f (u, v).
s
1 2
3
t
4
10
11/16
12/121/4
8/13
4/9
11/14
15/20
7/7
4/4
Например, для сети
остаточная сеть будет:
Фактически, остаточная сеть показывает, какой дополнительный поток можно
пропустить в данной сети.
Дополняющий путь – это простой путь из истока в сток в остаточной сети. Если
такой путь существует, то поток в сети можно увеличить на величину минимальной
нагрузки на ребро в дополняющем пути.
7
s
1 2
3
t
4
5
5
11
3
11
8
12
5
4
3
11
5
15
4
19Глава 8. Графы.
Метод Форда – Фалкерсона
s
1 2
3
t
4
Будем искать дополняющие пути из истока в сток, по которому можно пропустить
дополнительное количество вещества. Тогда схема алгоритма может быть записана
следующим образом:
10
11/16
12/12
1/4
8/13
4/9
11/14
15/20
7/7
4/4
f = 0;
while (существует дополняющий путь p) {
дополнить f вдоль p;
}
s
1 2
3
t
4
11
5
12
3
5
4
3
5
7
4
11 15
11
5
8
s
1 2
3
t
4
10
11/16
12/12
1/4
12/13
9
11/14
19/20
7/7
4/4
20Глава 8. Графы.
Теорема о минимальном разрезе
Пусть в сети G задан разрез (S, T). Пропускная способность разреза c (S, T) – это
сумма пропускных способностей ребер, пересекающих разрез в направлении от
истока к стоку. Разрез называется минимальным, если он имеет минимальную
пропускную способность из всех возможных разрезов.
Если в сети G задан поток, то для данного разреза c (S, T) можно рассматривать
поток через разрез – сумму потоков f (S, T) по ребрам, пересекающим разрез.
s
1 2
3
t
4
10
11/16
12/12
1/4
8/13
4/9
11/14
15/20
7/7
4/4
c (S, T) = 26
f (S, T) = 19
Теорема: если в сети G задан разрез (S, T), и поток f, то следующие утверждения
равносильны:
1. Поток f максимален
2. В остаточной сети Gf нет дополняющих путей.
3. Поток f равен пропускной способности минимального разреза в сети.
21Глава 8. Графы.
Пример реализации метода Форда – Фалкерсона
s
1 2
3
t
4
10
16
12
4
13
9
14
20
7
4
s
1 2
3
t
4
10
12/16
12/12
4
7/13
9
7/14
19/20
7/7
4
12/16
12/12
12/20
s
1 2
3
t
4
10
4
12
4
13
9
14
8
7
4
12
12
s
1 2
3
t
4
10
12/16
12/12
4
11/13
9
11/14
19/20
7/7
4/4
s
1 2
3
t
4
10
4
12
4
6 9
7
1
7
4
19
12
7
7
s
1 2
3
t
410
4
12
4
4 9
3
1
7
4
19
12
11
11
22Глава 8. Графы.
Анализ метода Форда – Фалкерсона
s
1
2
t
1
1 000 000
Если выбор дополняющего пути производится не очень удачно, то процедура поиска
максимального потока может затянуться.
1 000 000 1 000 000
1 000 000
1/1 000 000
1/1 000 000
1/1
s
1
2
t
1
1 000 000
1 000 000 999 999
999 999
1
1
s
1
2
t
1
1/1 000 000
1/1 000 000
1/1 000 000
1/1 000 000
и так далее, всего 2 000 000 шагов…
Можно попробовать искать кратчайший (по числу ребер) дополняющий путь между
истоком и стоком. Например, с помощью поиска в ширину. Получающийся при этом
алгоритм называется алгоритмом Эдмондса – Карпа).
Можно показать, что в алгоритме Эдмондса – Карпа число шагов ограничено сверху
числом 2nm, где m – число ребер в сети, а n – число вершин. Поскольку поиск идет
в ширину, то изменение потоков вдоль ребер и построение остаточной сети требуют
времени O(m), и общее время работы алгоритма можно оценить как O(m2
n).
23Глава 8. Графы.
Сеть с несколькими истоками и стоками
s1
1
2 t1
10
3
Если в графе есть несколько истоков и/или несколько стоков, то задача нахождения
максимального потока в такой сети сводится к задаче с одним истоком и одним стоком.
12 21
8
s2
s3
3
4
4
t2
5
6
11
7
12
9
15
12
13
18
22
15
13
17
27
17
18
17
20
s t
∞
∞
∞
∞
∞
24
Максимальное паросочетание в двудольном графе
Метод Форда – Фалкерсона можно использовать и для решения других задач,
например, для поиска максимального паросочетания в двудольном графе.
a2
a1
a3
b1
Паросочетание – это множество ребер в
двудольном графе, при котором каждая
вершина связана не более, чем с одним
ребром из этого множества.
a4
a5
b2
b3
b4
Максимальное паросочетание – это
паросочетание с наибольшим числом ребер.
25Глава 8. Графы.
Применение метода Форда – Фалкерсона для нахождения максимального
паросочетания
a2
a1
a3
b1
Добавим в граф две вершины – исток и сток – так, как показано на рисунке.
Пропускную способность всех ребер полагаем равной единице. Тогда максимальный
поток определит максимальное паросочетание в этом графе.
a4
a5
b2
b3
b4
s t
26Глава 8. Графы.
Задача коммивояжера
Задача: для заданного нагруженного неориентированного графа найти гамильтонов
цикл минимальной стоимости. Гамильтонов цикл – это цикл, содержащий все
вершины графа по одному разу.
Интерпретация: для заданного множества городов и дорог между ними найти
маршрут, позволяющий выйти из заданного города, обойти все города по одному
разу и вернуться обратно, пройдя минимально возможное расстояние.
3 2
321
3
3
4
4
5 5
6
S = 24S = 26
a
b
c
d
e
f g
h
S ≈ 14.7
Часто задача решается в условиях, когда выполнено неравенство треугольника:
для любых u, v и w: s(u, w) ≤ s(u, v) + s(v, w), где s – нагрузочная функция.
27
a
b
c
d
e
f g
h
1. Возможен простой, но очень неэффективный переборный алгоритм: найти все
гамильтоновы циклы, выбрав из них наименьший по стоимости.
2. Возможен «жадный» алгоритм, дающий результат не более, чем в 2 раза
худший оптимального.
3. Возможен алгоритм построения пути по минимальному скелету, который также
дает время работы не более, чем в 2 раза худшее оптимального.
4. Популярным способом решения задачи является «оптимизированный» перебор
или «метод ветвей и границ», при котором пытаются отсечь заведомо
неэффективные пути. Этот метод дает хорошие результаты на практике, однако,
в вырожденных случаях (например, полный граф, все ребра которого имеют
единичную нагрузку) сводится к полному перебору.
Алгоритмы решения задачи коммивояжера
Жадный алгоритм на каждом шаге пытается
добавить в имеющийся цикл одну вершину,
ближайшую к циклу. Этот алгоритм хорошо
работает в случае полного графа.
S = 4S = 6.58S = 9.4S = 10.58S = 11.4S = 13.4S = 15.86
Мы не получили оптимального маршрута.
Оптимальный маршрут имеет сумму S = 14.7.
Время работы алгоритма пропорционально
n2
log n
Глава 8. Графы.
28
a
b
c
d
e
f g
h
Алгоритм «минимального скелета» для задачи коммивояжера
Сначала построим минимальный скелет,
начав с произвольной вершины.
Теперь построим обход получившегося
дерева (который НЕ есть гамильтонов цикл).
Далее будем спрямлять этот путь, убирая
вершины, встречающиеся на этом пути дважды.
Получили путь длиной S = 18.6, что тоже хуже, чем оптимальный путь с длиной
S = 14.7
Заметим, что спрямление пути – это, фактически, обход дерева в глубину, поэтому
его можно получить быстрее, чем мы это делали.
Время работы алгоритма определяется временем построения минимального
скелета. Для случая полного графа (как в нашем случае) – это O(n2
), что лучше,
чем для жадного алгоритма увеличения цикла. Результат, однако, получился хуже.
Легко видеть, что двойное прохождение минимального скелета не хуже, чем вдвое
хуже оптимального гамильтонова пути, а если неравенство треугольника
выполнено, то спрямления всегда не ухудшают длину пути.
Глава 8. Графы.

графы

  • 1.
  • 2.
    2 8.3. Алгоритм Флойда- Уоршалла Транзитивное замыкание графа отношения. Ориентированный граф – это отношение. Это отношение транзитивно, если ∀u, v, w: u R v, v R w ⇒ u R w Транзитивное замыкание отношения – пополнение отношения новыми парами так, чтобы пополненное отношение стало транзитивным (необходимо добавить минимальное число таких пар). 1 3 6 4 2 7 5 Отношение не транзитивноОтношение транзитивно Задача нахождения транзитивного замыкания на языке графов: провести новую дугу из u в v, если в исходном графе существовал путь из u в v.
  • 3.
    3 Транзитивное замыкание графаотношения. Алгоритм «умножения матриц». 1 3 6 4 2 7 5 0 0 0 0 0 1 0 0 0 0 0 1 0 1 1 0 0 0 0 0 0 0 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 2 1 4 3 6 5 7 1 2 3 4 5 6 7 Пусть матрица G(l) представляет собой граф путей длиной l (то есть в матрице единица находится в ячейке (u,v), если в исходном графе существовал путь из u в v длиной не больше l ). Тогда матрица G(1) – это матрица смежности исходного графа G, G(n) – матрица смежности его транзитивного замыкания (очевидно, что если в графе существует путь длины, большей n, то существует и путь, длины не большей n). Алгоритм нахождения транзитивного замыкания: если удается вычислить G(l+1) по G(l) , то можно, начав с матрицы G, за n шагов получить матрицу G(n) .
  • 4.
    4 Алгоритм «умножения матриц». Пустьматрица G(l) представляет собой граф путей длиной l (то есть в матрице G(l) единица находится в ячейке (u,v), если в исходном графе существовал путь из u в v длиной не больше l ). Тогда что такое матрица G(l+1) ? u v l +1 w l G(l+1) [u,v] = 1, если найдется w такое, что либо G(l) [u,v] = 1, либо G(l) [u,w] = 1 и G [w,v] = 1. G(l+1) [u,v] = G(l) [u,v] + G(l) [u,w] × G [w,v]∑= n w 1 (умножение и сложение понимаются в смысле логических операций «и» и «или») В матричном виде: G(l+1) = G(l) + G(l) × G На самом деле количество операций можно сократить, если заметить, что длины путей можно каждый раз увеличивать вдвое: G(2l) = G(l) + G(l) × G(l) то есть «в матрице существует путь между вершинами u и v длиной не более 2l, если уже существовал путь длиной не более l или если найдется такая вершина w, что был путь от u до w и от w до v, оба длиной не больше l.
  • 5.
    5Глава 8. Графы. Алгоритм«умножения матриц». public static boolean[][] multiply (boolean[][] matr1, boolean[][] matr2) { int n = matr1.length; boolean[][] matr = new boolean[n][n]; for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { matr[i][j] = false; for (int k = 0; k < n; k++) { matr[i][j] ||= matr1[i][k] && matr2[k][j]; }}} return matr; } public static boolean[][] add (boolean[][] matr1, boolean[][] matr2) { int n = matr1.length; boolean [][] matr = new boolean[n][n]; for (int i = 0; i < n; ++i) for (int j = 0; j < n; ++j) matr[i][j] = matr1[i][j] + matr2[i][j]; } public static boolean[][] transClosure(boolean[][] G) { boolean[][] Gl = G; for (int l = 1; l < n; l*=2) Gl = add(Gl, multiply(Gl, Gl)); }
  • 6.
    6 Алгоритм Флойда –Уоршалла Алгоритм умножения матриц требует порядка n3 log n простых логических операций. Алгоритм Флойда – Уоршалла требует лишь n3 простых операций и не требует дополнительной памяти под промежуточные матрицы. Пусть матрица G(l) представляет собой граф путей, проходящих через промежуточные вершины с номерами от 0 до l -1 (то есть в матрице G(l) единица находится в ячейке (u,v), если в исходном графе существовал путь из u в v, проходящий только через вершины из множества {0,… l -1}, u и v в это множество не входят). Тогда что такое матрица G(l+1) ? u v {0,1,…l } u v {0,1,…l -1} 1) G(l+1) [u,v] = 1, если G(l) [u,v] == 1 u v {0,1,…l -1} 2) G(l+1) [u,v] = 1, если G(l) [u,l] == 1 && G(l) [l,v] == 1l {0,1,…l -1} G(l+1) [u,v] = G(l) [u,v] || G(l) [u,l] && G(l) [l,v]
  • 7.
    7 Алгоритм Флойда –Уоршалла нахождения транзитивного замыкания G( l+1) [u,v] = G( l) [u,v] || G( l) [u,l] && G( l) [l,v]    == == =+ 1],[,],[],[ 0],[,],[ ],[ )()()( )()( )1( luGеслиvlGvuG luGеслиvuG vuG lll ll l ||    == == =+ 1],[,][][ 0],[,][ ][ )()()( )()( )1( luGеслиlGuG luGеслиuG uG lll ll l || При u = l всегда G(l+1) [u] = G(l) [u] public static boolean[][] transClosure (boolean[][] G) { int n = G.length; for (int l = 0; l < n; l++) { // Формирование матрицы G(l+1): for (int u = 0; u < n; u++) { if (G[u][l]) { for (int v = 0; v < n; v++) { G[u][v] ||= G[l][v]; } } } } return G; }
  • 8.
    8Глава 8. Графы. Применениеалгоритма Флойда – Уоршалла для поиска кратчайших путей Пусть матрица G(l) представляет собой граф кратчайших путей, проходящих через промежуточные вершины с номерами от 0 до l -1. То есть в матрице G(l) в ячейке (u,v) находится длина кратчайшего пути из u в v, если он существовал, проходящий только через вершины из множества {0,… l -1}, u и v в это множество не входят. Если пути из u в v не было, то в соответствующей ячейке матрицы будет значение ∞. G(l+1) [u,v] = min(G(l ) [u,l] + G(l ) [l,v], G(l ) [u,v])u v {0,1,…l -1} l {0,1,…l -1} {0,1,…l -1} public static double[][] minPathsMatrix (double[][] G) { int n = G.length; for (int l = 0; l < n; l++) { // Формирование матрицы G(l+1): for (int u = 0; u < n; u++) { if (G[u][l] < Double.MAX_VALUE) { for (int v = 0; v < n; v++) { if (G[l][v] < Double.MAX_VALUE) G[u][v] = Math.min(G[u][l] + G[l][v] , G[u][v]); }}}} return G; }
  • 9.
    9 Применение алгоритма Флойда– Уоршалла для поиска кратчайших путей Помимо длин путей необходимо найти еще и матрицу направлений (аналог дерева предшествования для случая поиска путей из одной вершины). P [u,v] = p, где p – первая вершина на кратчайшем пути из u в v. P(0) [u,v] = v, если G [u,v] < ∞ и не определено, если G [u,v] = ∞. P(l +1) [u,v] = P(l ) [u,v], если не было коррекции кратчайшего пути. P(l +1) [u,v] = P(l ) [u,l ], если была коррекция кратчайшего пути. public static int[][] minPathsMatrix (double[][] G) { int n = G.length; int P[][] = new int[n][n]; for (int i = 0; i < n; i++) for (int j = 0; j < n; j++) if (G[i][j] < Double.MAX_VALUE) P[i][j] = j; for (int l = 0; l < n; l++) { for (int u = 0; u < n; u++) { if (G[u][l] < Double.MAX_VALUE) { for (int v = 0; v < n; v++) { if (G[l][v] < Double.MAX_VALUE) { if (G[u][l] + G[l][v] < G[u][v]) { G[u][v] = G[u][l] + G[l][v]; P[u][v] = P[u][l]; } } } } } } return P; }
  • 10.
    10Глава 8. Графы. АлгоритмДжонсона для разреженных графов Покажем, что задачу о поиске минимальных путей в произвольном графе без циклов с отрицательным весом можно свести к задаче о поиске путей в графе с неотрицательными весами ребер. Выберем произвольную функцию h(u), которая приписывает вещественные числа всем вершинам графа. Заменим вес каждого ребра w(u,v) на w1(u,v) = w(u,v) + h(u) – h(v) Тогда общий вес любого пути из s в f изменится на постоянную величину h(s) – h(f) Осталось подобрать величины h(u) таким образом, чтобы веса всех ребер стали неотрицательными. Если в графе веса всех ребер неотрицательны, то для нахождения всех кратчайших путей между всеми парами вершин можно n раз применить алгоритм Дейкстры. Время работы такого алгоритма составляет порядка n (m + n) log n. Если ребер в графе немного (порядка n), то это время может оказаться лучше, чем для алгоритма Флойда – Уоршалла, который занимает время порядка n3 .
  • 11.
    11 Алгоритм Джонсона дляразреженных графов 0 0 -1 0 -3-4 -3 -2 -1 2 2 3 -3 2 -1 3 2 2 -1 2 4 -1 2 1 -2 3 2 2 1. Проведем дополнительные дуги нулевой длины из новой вершины во все вершины графа. 2. Вычислим длины минимальных путей из новой вершины во все остальные (например, с помощью алгоритма Беллмана – Форда). 3. Положим h(u) равными вычисленным длинам путей. Модифицированные длины ребер показаны на рисунке красным цветом. 5 00 3 0 0 2 1 0 0 2 6 2 0 1 0 5 3 2 Алгоритм Беллмана – Форда имеет время работы порядка mn, поэтому общее время нахождения всех кратчайших путей будет порядка mn log n.
  • 12.
    12Глава 8. Графы. Построениеминимального скелета нагруженного графа. Алгоритм Прима. 10 2 3 6 1 85 7 4 9 1 1 1 2 2 2 3 4 3 3 4 4 5 5 5 Запускаем алгоритм обхода графа, начиная с произвольной вершины. В качестве контейнера выбираем очередь с приоритетами. Приоритет – текущая величина найденного расстояния до уже построенной части опорного дерева. Релаксации (как в Алгоритме Дейкстры) подвергаются прямые и обратные ребра. 1 2 3 4 5 6 7 8 9 10n π 0 ∞ ∞ ∞ ∞ ∞ ∞ ∞ ∞ ∞d 1 3 1 1 2 2 2 2 2 25 44 5 3 10 1 3 4 6 3 4 1 9 2 8 Оценка времени работы алгоритма – O((m + n) log n) – коррекция очереди с приоритетами происходит при присоединении каждой новой вершины и при анализе каждого нового ребра. В результате работы получили список ребер опорного дерева (скелета) вместе с нагрузками на все ребра.
  • 13.
    13 Построение минимального скелетанагруженного графа. Алгоритм Крускала. 10 2 3 6 1 85 7 4 9 1 3 5 2 2 4 1 4 3 3 2 4 5 5 1 1. Строим список ребер, упорядоченный по возрастанию нагрузки 1-2 3-6 8-9 2-5 2-10 8-7 1-4 4-9 3-10 2-8 5-6 6-7 2-3 6-10 4-8 1 1 1 2 2 2 3 3 3 4 4 4 5 5 5 2. Строим лес из изолированных вершин 3. Добавляем ребра по порядку, следя за тем, чтобы концы ребер лежали в разных деревьях 1 2 3 4 5 6 7 8 9 10 0 0 0 0 0 0 0 0 0 0 1 2 3 4 5 6 7 8 9 10 2 4 4 4 7 7 3 7 7
  • 14.
    14 Сети и потоки s 12 3 t 4 Сеть – это ориентированный нагруженный граф, в котором нагрузка имеет интерпретацию «пропускная способность» (разумеется, положительная; можно считать, что отсутствующая дуга соответствует нулевой нагрузке). Будем обозначать эту нагрузку c (u, v). 16 10 13 4 12 9 14 20 7 4 Мы будем считать, что a) в сети есть две выделенные вершины – исток (только исходящие дуги) и сток (только входящие дуги); b) любая вершина лежит на каком-нибудь пути из истока в сток (нет «бесполезных» вершин).
  • 15.
    15Глава 8. Графы. Сетии потоки s 1 2 3 t 4 16 10 13 4 12 9 14 20 7 4 Поток в сети – это задание некоторой дополнительной нагрузки на дуги, обладающей свойствами: a) поток по дуге не может превышать пропускной способности дуги и всегда неотрицателен; b) в любую вершину (кроме истока и стока) количество втекающей жидкости равно количеству вытекающей. 11/16 12/12 1/4 8/13 4/9 11/14 15/20 7/7 4/4 Интерпретация потока: количество жидкости, которое в действительности проходит по заданным каналам (дугам).
  • 16.
    16Глава 8. Графы. Формальноеопределение потока s 1 2 3 t 4 Пусть задана сеть. Зададим функцию f (u, v), обладающую следующими свойствами: 10 b) f (u, v) = - f (v, u) – поток – это кососимметричная функция c) для любой вершины u кроме истока и стока На приведенной выше картинке: f (S, 3) = 8; f (3, 2) = -4; f (1, 3) = -1. Для каждой дуги (при заданном потоке f) определим также ее «остаточную пропускную способность»: cf(u, v) = c (u, v) – f(u, v) Например, для заданного потока cf(3, 4) = 3, cf(1, 3) = 11. 11/16 12/12 1/4 8/13 4/9 11/14 15/20 7/7 4/4 a) f (u, v) ≤ c(v, u) – поток не превышает пропускной способности дуги 0),( =∑∈Vv vuf Функция потока f имеет смысл: количество вещества, перемещающегося из вершины u в вершину v. Из свойств (a) и (b) следует, что если вершины u и v не соединены дугой, то f(u, v) = 0. Из этого следует, что в формуле (с) можно ограничиться инцидентными дугами.
  • 17.
    17Глава 8. Графы. Леммао потоке в сети Обозначение: будем писать f (X, Y), где X и Y – множества вершин, подразумевая под этим ∑∑∈ ∈Xx Yy yxf ),( На самом деле в эту сумму входят только значения потока для тех вершин x и y, для которых имеется дуга, ведущая из x в y. Тогда для любого потока f справедливы следующие соотношения: f (X, X) = 0 для любого X ⊂ V f (X, Y) = – f (Y, X) для любых X, Y ⊂ V f (X ∪ Y, Z) = f (X, Z) + f (Y, Z) для любых X, Y, Z ⊂ V при X ∩ Y = ∅ Назовем величиной потока | f | = f (s, V) Покажем, что | f | = f (V, t) f (s, V) = f (V, V) – f (V s, V) = f (V, V s) = f (V, t) + f (V, V s t) = f (V, t) Разрез в сети – это разбиение множества вершин V на два подмножества S и T такие, что s ∈ S, t ∈T. Очевидно, что поток через любой разрез f (S, T) = | f |. Действительно, f (S, T) = f (S, V) – f (S, S) = f (S, V) = f (s, V) + f (S s, V) = f (s, V) = | f | Например, закон сохранения потока можно коротко записать так: f (u, V) = 0 для всех u ∈ V – { s, t }
  • 18.
    18Глава 8. Графы. Остаточнаясеть Для заданного потока f (u, v) в сети с пропускной способностью ребер c (u, v) построим остаточную сеть r (u, v) = c (u, v) – f (u, v). s 1 2 3 t 4 10 11/16 12/121/4 8/13 4/9 11/14 15/20 7/7 4/4 Например, для сети остаточная сеть будет: Фактически, остаточная сеть показывает, какой дополнительный поток можно пропустить в данной сети. Дополняющий путь – это простой путь из истока в сток в остаточной сети. Если такой путь существует, то поток в сети можно увеличить на величину минимальной нагрузки на ребро в дополняющем пути. 7 s 1 2 3 t 4 5 5 11 3 11 8 12 5 4 3 11 5 15 4
  • 19.
    19Глава 8. Графы. МетодФорда – Фалкерсона s 1 2 3 t 4 Будем искать дополняющие пути из истока в сток, по которому можно пропустить дополнительное количество вещества. Тогда схема алгоритма может быть записана следующим образом: 10 11/16 12/12 1/4 8/13 4/9 11/14 15/20 7/7 4/4 f = 0; while (существует дополняющий путь p) { дополнить f вдоль p; } s 1 2 3 t 4 11 5 12 3 5 4 3 5 7 4 11 15 11 5 8 s 1 2 3 t 4 10 11/16 12/12 1/4 12/13 9 11/14 19/20 7/7 4/4
  • 20.
    20Глава 8. Графы. Теоремао минимальном разрезе Пусть в сети G задан разрез (S, T). Пропускная способность разреза c (S, T) – это сумма пропускных способностей ребер, пересекающих разрез в направлении от истока к стоку. Разрез называется минимальным, если он имеет минимальную пропускную способность из всех возможных разрезов. Если в сети G задан поток, то для данного разреза c (S, T) можно рассматривать поток через разрез – сумму потоков f (S, T) по ребрам, пересекающим разрез. s 1 2 3 t 4 10 11/16 12/12 1/4 8/13 4/9 11/14 15/20 7/7 4/4 c (S, T) = 26 f (S, T) = 19 Теорема: если в сети G задан разрез (S, T), и поток f, то следующие утверждения равносильны: 1. Поток f максимален 2. В остаточной сети Gf нет дополняющих путей. 3. Поток f равен пропускной способности минимального разреза в сети.
  • 21.
    21Глава 8. Графы. Примерреализации метода Форда – Фалкерсона s 1 2 3 t 4 10 16 12 4 13 9 14 20 7 4 s 1 2 3 t 4 10 12/16 12/12 4 7/13 9 7/14 19/20 7/7 4 12/16 12/12 12/20 s 1 2 3 t 4 10 4 12 4 13 9 14 8 7 4 12 12 s 1 2 3 t 4 10 12/16 12/12 4 11/13 9 11/14 19/20 7/7 4/4 s 1 2 3 t 4 10 4 12 4 6 9 7 1 7 4 19 12 7 7 s 1 2 3 t 410 4 12 4 4 9 3 1 7 4 19 12 11 11
  • 22.
    22Глава 8. Графы. Анализметода Форда – Фалкерсона s 1 2 t 1 1 000 000 Если выбор дополняющего пути производится не очень удачно, то процедура поиска максимального потока может затянуться. 1 000 000 1 000 000 1 000 000 1/1 000 000 1/1 000 000 1/1 s 1 2 t 1 1 000 000 1 000 000 999 999 999 999 1 1 s 1 2 t 1 1/1 000 000 1/1 000 000 1/1 000 000 1/1 000 000 и так далее, всего 2 000 000 шагов… Можно попробовать искать кратчайший (по числу ребер) дополняющий путь между истоком и стоком. Например, с помощью поиска в ширину. Получающийся при этом алгоритм называется алгоритмом Эдмондса – Карпа). Можно показать, что в алгоритме Эдмондса – Карпа число шагов ограничено сверху числом 2nm, где m – число ребер в сети, а n – число вершин. Поскольку поиск идет в ширину, то изменение потоков вдоль ребер и построение остаточной сети требуют времени O(m), и общее время работы алгоритма можно оценить как O(m2 n).
  • 23.
    23Глава 8. Графы. Сетьс несколькими истоками и стоками s1 1 2 t1 10 3 Если в графе есть несколько истоков и/или несколько стоков, то задача нахождения максимального потока в такой сети сводится к задаче с одним истоком и одним стоком. 12 21 8 s2 s3 3 4 4 t2 5 6 11 7 12 9 15 12 13 18 22 15 13 17 27 17 18 17 20 s t ∞ ∞ ∞ ∞ ∞
  • 24.
    24 Максимальное паросочетание вдвудольном графе Метод Форда – Фалкерсона можно использовать и для решения других задач, например, для поиска максимального паросочетания в двудольном графе. a2 a1 a3 b1 Паросочетание – это множество ребер в двудольном графе, при котором каждая вершина связана не более, чем с одним ребром из этого множества. a4 a5 b2 b3 b4 Максимальное паросочетание – это паросочетание с наибольшим числом ребер.
  • 25.
    25Глава 8. Графы. Применениеметода Форда – Фалкерсона для нахождения максимального паросочетания a2 a1 a3 b1 Добавим в граф две вершины – исток и сток – так, как показано на рисунке. Пропускную способность всех ребер полагаем равной единице. Тогда максимальный поток определит максимальное паросочетание в этом графе. a4 a5 b2 b3 b4 s t
  • 26.
    26Глава 8. Графы. Задачакоммивояжера Задача: для заданного нагруженного неориентированного графа найти гамильтонов цикл минимальной стоимости. Гамильтонов цикл – это цикл, содержащий все вершины графа по одному разу. Интерпретация: для заданного множества городов и дорог между ними найти маршрут, позволяющий выйти из заданного города, обойти все города по одному разу и вернуться обратно, пройдя минимально возможное расстояние. 3 2 321 3 3 4 4 5 5 6 S = 24S = 26 a b c d e f g h S ≈ 14.7 Часто задача решается в условиях, когда выполнено неравенство треугольника: для любых u, v и w: s(u, w) ≤ s(u, v) + s(v, w), где s – нагрузочная функция.
  • 27.
    27 a b c d e f g h 1. Возможенпростой, но очень неэффективный переборный алгоритм: найти все гамильтоновы циклы, выбрав из них наименьший по стоимости. 2. Возможен «жадный» алгоритм, дающий результат не более, чем в 2 раза худший оптимального. 3. Возможен алгоритм построения пути по минимальному скелету, который также дает время работы не более, чем в 2 раза худшее оптимального. 4. Популярным способом решения задачи является «оптимизированный» перебор или «метод ветвей и границ», при котором пытаются отсечь заведомо неэффективные пути. Этот метод дает хорошие результаты на практике, однако, в вырожденных случаях (например, полный граф, все ребра которого имеют единичную нагрузку) сводится к полному перебору. Алгоритмы решения задачи коммивояжера Жадный алгоритм на каждом шаге пытается добавить в имеющийся цикл одну вершину, ближайшую к циклу. Этот алгоритм хорошо работает в случае полного графа. S = 4S = 6.58S = 9.4S = 10.58S = 11.4S = 13.4S = 15.86 Мы не получили оптимального маршрута. Оптимальный маршрут имеет сумму S = 14.7. Время работы алгоритма пропорционально n2 log n Глава 8. Графы.
  • 28.
    28 a b c d e f g h Алгоритм «минимальногоскелета» для задачи коммивояжера Сначала построим минимальный скелет, начав с произвольной вершины. Теперь построим обход получившегося дерева (который НЕ есть гамильтонов цикл). Далее будем спрямлять этот путь, убирая вершины, встречающиеся на этом пути дважды. Получили путь длиной S = 18.6, что тоже хуже, чем оптимальный путь с длиной S = 14.7 Заметим, что спрямление пути – это, фактически, обход дерева в глубину, поэтому его можно получить быстрее, чем мы это делали. Время работы алгоритма определяется временем построения минимального скелета. Для случая полного графа (как в нашем случае) – это O(n2 ), что лучше, чем для жадного алгоритма увеличения цикла. Результат, однако, получился хуже. Легко видеть, что двойное прохождение минимального скелета не хуже, чем вдвое хуже оптимального гамильтонова пути, а если неравенство треугольника выполнено, то спрямления всегда не ухудшают длину пути. Глава 8. Графы.