теория графов
Post on 23-Nov-2014
233 Views
Preview:
TRANSCRIPT
Теория графов
Родоначальником теории графов принято считать математика Леонарда Эйлера (1707-
1783). Однако теория графов многократно переоткрывалась разными авторами при решении
различных прикладных задач.
Задача о Кенигсбергских мостах. На рис. 1 представлен схематический план
центральной части города Кенигсберг (ныне Калининград), включающий два берега реки
Перголя, два острова в ней и семь соединяющих мостов. Задача состоит в том, чтобы обойти
все четыре части суши, пройдя по каждому мосту один раз, и вернуться в исходную точку. Эта
задача была решена (показано, что решение не существует) Эйлером в 1736 году.
рис. 1
Задача о трех домах и трех колодцах. Имеется три дома и три колодца, каким-то
образом расположенные на плоскости. Провести от каждого дома к каждому колодцу
тропинку так, чтобы тропинки не пересекались (рис. 2). Эта задача была решена (показано,
что решение не существует) Куратовским в 1930 году.
рис. 2
Неформально, граф можно определить как набор вершин (города, перекрестки,
компьютеры, буквы, цифры кости домино, микросхемы, люди) и связей между ними: дороги
между городами; улицы между перекрестками; проводные линии связи между компьютерами;
слова, начинающиеся на одну букву и закачивающиеся на другую или эту же букву;
проводники, соединяющие микросхемы; родственные отношения, например, Алексей - сын
Петра.
1
ОСНОВНЫЕ СВОЙСТВА ГРАФОВ
Определение: Графом G(V,E) называется совокупность двух множеств – непустого
множества V (множества вершин) и множества E двухэлементных подмножеств множества V
(E – множество ребер).Говорят, что ребро (a, b) соединяет вершины a и b. Если ребро e соединяет вершину a с
вершиной b и пара (a,b) считается упорядоченной, то это ребро называется ориентированным,
вершина a – его началом, вершина b – концом. Если же эта пара считается неупорядоченной,
то ребро называется неориентированным, а обе вершины – его концами.
Ориентированным называется граф, в котором – множество упорядоченных
пар вершин вида (x,y), где x называется началом, а y – концом дуги. Дугу (x, y) записывают как
.
Если элементом множества E может быть пара одинаковых (не различных) элементов V,
то такой элемент множества E называется петлей, а граф называется графом с петлями (или
псевдографом).
Вершины, соединенные ребром, называются смежными. Ребра, имеющие общую
вершину, также называются смежными.
Ребро и любая из его двух вершин называются инцидентными.
Степенью вершины в неорентированном графе называется количество ребер,
соединяющих ее с другими вершинами. Вершина, степень которой равна 0, называется
изолированной. В ориентированном графе степень вершины равна сумме ее входящей и
исходящей степеней.
Маршрут в графе – это последовательность вершин x1, x2, …, xn, такая, что для каждого
i = 1, 2, …, n-1 вершины xi и xi+1 соединены ребром. Эти n-1 ребер называются ребрами
маршрута. Говорят, что маршрут проходит через них, а число n-1 называют длиной
маршрута. Говорят, что маршрут соединяет вершины x1 и xn, они называются соответственно
началом и концом маршрута, вершины x2, …, xn-1 называются промежуточными. Маршрут
называется замкнутым, если x1 = xn.
Путь – это маршрут, в котором все ребра различны. Путь называется простым, если и
все вершины в нем различны.
Цикл – это замкнутый путь. Цикл x1, x2, …, xn-1, x1 называется простым, если все
вершины x1, x2, …, xn-1 попарно различны. Граф без циклов называется ациклическим.
В графе на рис.3 последовательность вершин
2
2, 3, 5, 4 – не маршрут;
2, 3, 4, 5, 1, 4, 3 – маршрут, но не путь;
3, 1, 4, 5, 1, 2 – путь, но не простой;
2, 3, 1, 4, 5, 1, 2 – цикл, но не простой;
2, 3, 4, 5, 1, 2 – простой цикл.
Рис. 3. Обыкновенный граф.
Неориентированный граф называется связным, если в нем для любых двух вершин
имеется маршрут, соединяющий эти вершины. Ориентированный граф называется связанным,
если для любых двух вершин a и b имеется маршрут от a до b и маршрут от b до a.
Множество всех достижимых вершин называются связными компонентами графа.
Некоторые виды графов имеют специальные названия.
Полным называется неориентированный граф, в котором каждая пара вершин являются
смежными.
Ациклический неориентированный граф называется лесом, а связанный ациклический
неориентированный граф – деревом.
ПРЕДСТАВЛЕНИЕ ГРАФА
Для представления графа в памяти компьютера используются следующие способы:
1) в виде множества списков смежных вершин;
2) в виде матрицы смежности.
Списки смежности.
Представление графа в виде списков смежности использует массив из |V| списков, по
одному для каждой вершины из V. Для каждой вершины a перечисляются все смежные с ней
вершины, т.е. элементы множества V(a). Такой способ задания дает возможность быстрого
просмотра окрестности вершины. Например, на рис.4 представлен граф списками смежности,
в котором содержатся пары связанных между собой элементов (вершин): (1,2), (1,4), (2,3),
(2,5), (2,6), (3,6), (4,5)
3
1
2 4 53
212122
436543
123456
1 2
4 5
3
6
/
////
5 6 /
Рис. 4. Список смежности для неориентированного графа.
Матрица смежности.
Представление графа с помощью матрицы смежности предполагает, что вершины
пронумерованы в некотором порядке числами 1, 2, …, |V|. В этом случае представление графа
G с помощью матрицы смежности представляет собой матрицу A=(aij) размером |V|x|V| такую,
что
На рис.5 показан граф с занумерованными вершинами и его матрицы смежности.
Рис.5. Пример неориентированного графа.
Матрица смежности:
ОБХОД ГРАФОВ
Поиск в графе – это алгоритмический метод обхода графа, в основе которого лежит
систематический перебор вершин графа, такой что каждая вершина просматривается только
один раз.
1) Поиск в глубину (Depth First Search).
Введем следующие понятия:
Вершину, которая еще не посещена, будем называть новой. В результате посещения
вершина становится открытой и остается такой, пока не будут исследованы все инцидентные
ей ребра. После этого она превращается в закрытую.
Стратегия поиска в глубину состоит в том, чтобы идти вглубь графа, пока это
возможно. При выполнении поиска в глубину исследуются все ребра, выходящие из
последней открытой вершины, и мы покидаем вершину только тогда, когда не остается
неисследованных выходящих из нее ребер. При этом происходит возврат в вершину, из
которой была открыта текущая вершина. Этот процесс продолжается до тех пор, пока не
4
1
2 3
4 65
будут открыты все вершины, достижимые из исходной. Если при этом остаются неоткрытые
вершины, то одна из них выбирается в качестве новой исходной вершины, и поиск
возобновляется из нее. Процесс повторяется до тех пор, пока не будут открыты все вершины
графа.
При поиске в глубину в качестве активной выбирается та из открытых вершин, которая
была посещена последней. Для реализации такого правила выбора наиболее удобной
структурой хранения множества открытых вершин является стек: открываемые вершины
складываются в стек в том порядке, в каком они открываются, а в качестве активной
выбирается последняя вершина.
На рис.6 у вершин в скобках указана та очередность, в которой вершины графа
просматривались в процессе поиска в глубину.
Рис. 6. Пример обхода графа (поиск в глубину).
Рассмотрим процедуру реализующая обход графа в глубину:
Пусть элементы вектора Visited[1..n] определяет состояние вершины, т.е.
Visited[i]=true, если вершина просмотрена, false иначе.
Матрица A[n,n] определяет матрицу смежности заданного графа.
Program Depth_First;
var
A: array [1..20, 1..20] of 0..1;
visited: array [1..20] of boolean;
I,j,n:integer;
procedure dfs(v: integer); {v текущая вершина}
var i: integer;
begin
writeln(v); {вершина v не посещалась}
visited[v]:= true;
for i:= 1 to n do
if (a[v,i]=1) and (not visited[i]) then
5
dfs(i);
end;
procedure graph_dfs;
var i: integer;
begin
for i:= 1 to n do
visited[i]:=false;
for i:= 1 to n do
if not visited[i] then
dfs(i);
end;
begin
write('n= '); readln(n);
for i:=1 to n do
for j:=i+1 to n do
begin
write('Versina ',i,',',j,'= ');
readln(A[i,j]);
A[j,i]:=A[i,j]
end;
graph_dfs;
end.
Процедура помечает вершину V, затем проходит по всем вершинам и проверяет
каждую вершину, если она не помечена и смежная с данной вершиной V, то запускается от
нее.
2) Поиск в ширину (Breadth First Search)
Суть заключается в том, чтобы рассмотреть все вершины, связанные с текущей.
Принцип выбора следующей вершины для вершины a – выбирается та, которая была раньше
рассмотрена, т.е. находящиеся от нее на расстоянии 1, затем вершины, находящиеся от a на
расстоянии 2, и т.д.
Для реализации данного принципа необходима структура данных очередь, когда новая
вершина становится открытой, она добавляется в конец очереди, а активная выбирается в ее
начале.
Основная особенность поиска в ширину, отличающая его от других способов обхода
графов, состоит в том, что в качестве активной вершины выбирается та из открытых, которая
6
была посещена раньше других. Именно этим обеспечивается главное свойство поиска в
ширину: чем ближе вершина к старту, тем раньше она будет посещена.
На рис.7 рядом с вершинами в скобках указана очередность просмотра вершин графа.
Рис.7. Пример обхода графа (поиск в ширину).
Procedure DFS (m:Integer);
Var
Q:Array [1..20] Of integer; {Очередь}
head,tail:Integer;{Указатели очереди, head – номер текущей
вершины; tail – новые вершины, помещаемые в «хвост» очереди q}
visited: Array [1..20] Of Boolean; {отмечает уже пройденные
вершины}
i,v,k:Integer;
Begin
head:=1; tail:=1; {Начальная инициализация}
For i:=1 to n do
Visited[i]:=False;
q[tail]:=m;
visited[m]:=true; {B очередь помещаем вершину m}
while head<=tail do {Пока очередь не пуста} begin
v:=q[head];
head:=head+1;
write(v); {Берем элемент из очереди}
For k:=1 to n do {Просмотр всех вершин, связанных с
вершиной v}
if (A[v,k]<>0) and (not Visited[k]) then
{Если вершина ранее не просмотрена, то заносим её номер в
очередь}
begin
7
tail:=tail+1;
q[tail]:=k;
Visited[k]:=True;
end;
end;
End;
КРАТЧАЙШИЕ ПУТИ
Дан ориентированный граф G = <V,E>, веса дуг – A[i,j] (i,j=l..N, где N – количество
вершин графа), начальная и конечная вершины – s, t V. Веса дуг записаны в матрице
смежности А, если вершины i и j не связаны дугой, то A[i,j] = 0. Путь между s и t оценивается
суммой . Необходимо найти путь с минимальной оценкой. Оценку пути назовем
его весом или длиной.
Пример. Кратчайший путь в графе на рис.8. из вершины 1 в вершину 4 проходит через
3-ю и 2-ю вершины и имеет оценку 6.
Рис. 8. Ориентированный взвешенный граф.
Нам необходимо найти кратчайший путь, т. е. путь с минимальным весом, между
двумя вершинами графа. Эта задача разбивается на две подзадачи: сам путь и значение
минимального веса. Обозначим ее через D[s] как Array[1..N] Of Integer и на каждом шаге
определяем оценки от вершины s до всех остальных вершин графа.
Алгоритм Дейкстры
Дан ориентированный или неориентированный взвешенный граф с n вершинами и m
рёбрами. Веса всех рёбер неотрицательны. Указана некоторая стартовая вершина s. Требуется
найти длины кратчайших путей из вершины s во все остальные вершины и вывести сам
кратчайший путь.
Эта задача называется "задачей о кратчайших путях с единственным источником"
(single-source shortest paths problem).
Алгоритм
8
3
1
6
252
1 4
3
Опишем алгоритм, который предложил датский исследователь Дейкстра (Dijkstra) в
1959 г.
Определим массив d[], в котором для каждой вершины v будем хранить текущую длину
d[v] кратчайшего пути из s в v. Изначально записываем в d[s]=0 и в d[v] значения A[s,v], если
они достижимы из вершины s, а для всех остальных вершин эта длина равна бесконечности
(достаточно большое число, заведомо большее возможной длины пути): d[v] = ∞
Кроме того, для каждой вершины v будем хранить, помечена она ещё или нет, т.е.
определим массив visited[]. Изначально все вершины не помечены, т.е. visited [v]=true, если
вершина уже рассмотрена, и visited[v]=false, если нет. Изначально заполняем массив u
значениями false (вершины не обработаны) и visited [s]=true.
Определим массив p[], такой что p[v] – номер вершины, из которой нужно идти в
вершину v в текущем кратчайшем пути, т.е. предпоследняя вершина пути.
Изначально заполнить массив p значением s. Кратчайший путь можно будет
восстановить по нему, каждый раз беря предка от текущей вершины, пока мы не придём в
стартовую вершину s – так мы получим искомый кратчайший путь, но записанный в обратном
порядке.
P = (s, …, p[p[p[v]]], p[p[v]], p[v], v)
Алгоритм Дейкстры состоит из n итераций. На очередной итерации выбирается
вершина i с наименьшей величиной d[v] среди ещё не помеченных, т.е.:
На первой итерации выбрана будет стартовая вершина s.
Выбранная таким образом вершина v отмечается помеченной. Далее, на текущей
итерации, из вершины v производятся улучшения (релаксации): просматриваются все рёбра
(v,x), исходящие из вершины v, и для каждой такой вершины x алгоритм пытается улучшить
значение d[x], тогда:
d[x] = min (d[x], d[v] + A[v,x])
При каждой успешной релаксации, т.е. когда из выбранной вершины v происходит
улучшение расстояния до некоторой вершины x, мы записываем, что предком вершины x
является вершина v: p[x] = v
На этом текущая итерация заканчивается, алгоритм переходит к следующей итерации
(снова выбирается вершина с наименьшей величиной d, из неё производятся релаксации, и
т.д.). После n итераций, все вершины графа станут помеченными, и алгоритм свою работу
завершает. Утверждается, что найденные значения d[v] и есть искомые длины кратчайших
путей из s в v.
9
Инициализация:
1 2 3 4 5 6
Visited 1 0 0 0 0 0
D 0 7 9 ∞ ∞ 14
P 1 1 1 1 1 1
Шаг 1:
Р
Шаг 2:
10
1 2 3 4 5 6
Visited 1 1 0 0 0 0
D 0 7 9 22 ∞ 14
P 1 1 1 2 1 1
1 2 3 4 5 6
Visited 1 1 1 0 0 0
D 0 7 9 20 ∞ 11
P 1 1 1 3 1 3
Шаг 3:
Шаг 4 и Шаг 5 массивы не изменяются. В результате получим:
Маршрут из вершины 1 в вершину 5 будет:
Const
inf=65535;
type
matrix=array[1..50,1..50] of word;
var
D:array[1..100] of word; //массив кратчайших расстояний
P:array[1..100] of word; //массив вершин предков в кратчайшем
пути
visited:array[1..100] of boolean; //отмечаем, если посетили
A:Matrix;
s,i,j,n:integer;
procedure Deisktr (A : Matrix; N, s : integer);
var i, j, v, min, x, t, u: longint;
begin
for i:=1 to N do
begin
If A[s,i]<>0 then D[i]:=A[s,i] //изначальный массив расстояний
Else D[i]:=inf;
P[i]:=s;
11
1 2 3 4 5 6
Visited 1 1 1 0 0 1
D 0 7 9 20 20 11
P 1 1 1 3 6 3
1 2 3 4 5 6Visited 1 1 1 1 1 1
D 0 7 9 20 20 11P 1 1 1 3 6 3
visited[i]:=false;
end;
visited[s]:=TRUE; //вершина S посещена d[s]:=0;
for i:=1 to n-1 do // на каждом шаге находим минимальное решение и пытаемся его улучшить
begin
min:=inf;
for j:=1 to N do
if (not visited[j]) and (D[j] < min) then
begin
min:=D[j]; //минимальное расстояние
v:=j; //найденная вершина
end;
visited[v]:=TRUE; //и она отмечается посещенной
for x:= 1 to N do
if (D[x]>D[v]+A[v,x]) and (not visited[x]) and (A[v,x]<>0) then //пытаемся улучшить решение. Если в ней расстояние больше, чем сумма расстояния до текущей вершины и длины ребра, то уменьшаем его.
begin
D[x]:=D[v] + A[v,x];
P[x]:=v; //запоминаем откуда пришли
end;
end;
write('finis='); readln(t);
u:=t; write(u);
while u<>s do begin
u:=p[u]; write(' ',u);
end;
end;
begin
write('n=');
Readln(n);
for i:=1 to n do
for j:=i+1 to n do
begin
write('Rebro ',i,',',j,'= ');
12
readln(A[i,j]);
A[j,i]:=A[i,j];
end;
write('start='); readln(s);
Deisktr(a,n,s);
end.
13
top related