- •Алгоритмы на графах
- •3.1. Представление графа в памяти компьютера
- •3.2. Поиск в графе
- •3.2.1. Поиск в глубину
- •3.2.2. Поиск в ширину
- •3.3. Деревья
- •3.3.1. Основные понятия. Стягивающие деревья
- •3.3.2. Порождение всех каркасов графа
- •3.3.3. Каркас минимального веса. Метод Краскала
- •3.3.4. Каркас минимального веса. Метод Прима
- •3.4. Связность
- •3.4.1. Достижимость
- •3.4.2. Определение связности
- •3.4.3. Двусвязность
- •3.5. Циклы
- •3.5.1. Эйлеровы циклы
- •3.5.2. Гамильтоновы циклы
- •3.5.3. Фундаментальное множество циклов
- •3.6. Кратчайшие пути
- •3.6.1. Постановка задачи. Вывод пути
- •3.6.2. Алгоритм Дейкстры
- •3.6.3. Пути в бесконтурном графе
- •3.6.4. Кратчайшие пути между всеми парами вершин. Алгоритм Флойда
- •3.7. Независимые и доминирующие множества
- •3.7.1. Независимые множества
- •3.7.2. Метод генерации всех максимальных независимых множеств графа
- •3.7.3. Доминирующие множества
- •3.7.4. Задача о наименьшем покрытии
- •3.7.5. Метод решения задачи о наименьшем разбиении
- •3.8 Раскраски
- •3.8.1 Правильные раскраски
- •3.8.2. Поиск минимальной раскраски вершин графа
- •3.8.3. Использование задачи о наименьшем покрытии при раскраске вершин графа
- •3.9. Потоки в сетях, паросочетания
- •3.9.1. Постановка задачи
- •3.9.2. Метод построения максимального потока в сети
- •3.9.3. Наибольшее паросочетание в двудольном графе
- •3.10. Методы приближенного решения задачи коммивояжера
- •3.10.1. Метод локальной оптимизации
- •3.10.2. Алгоритм Эйлера
- •2.10.3. Алгоритм Кристофидеса
- •3.11. Задачи
3.9.2. Метод построения максимального потока в сети
Рассмотрим метод на примере. Пусть дана сеть G=(V,E), узлом-источником является вершина 1, узлом-стоком - вершина 6. Построим максимальный поток (F) между этими вершинами. Начальное значение F нулевое. Очевидно, что структурой данных для описания F является матрица того же типа, что и матрица С, в которой определены пропускные способности дуг.
2-й - [1,2] и 3-й - [1,6]. Что представляют из себя метки? Первая цифра - номер вершины, из которой идет поток, вторая цифра - численное значение потока, который можно передать по этой дуге. Второй шаг. Выберем помеченную, но не просмотренную вершину. Первой в соответствующей структуре данных записана вершина 2. Рассмотрим дуги, для которых она является началом - дуги (2,4) и (2,5). Вершины 4 и 5 не помечены. Присвоим им метки - [2,2] и [2,2]. Итак, на втором шаге вершина 2 просмотрена, вершины 3, 4, 5 помечены, но не просмотрены, остальные вершины не помечены. Третий шаг. Выбираем вершину 3. Рассмотрим дугу (3,4). Вершина 4 помечена. Переходим к следующей вершине - четвертой, соответствующая дуга - (4,6). Вершина 6 не помечена. Присваиваем ей метку [4,2]. Мы достигли вершины-стока, тем самым найдя путь (последовательность дуг), поток по которым можно увеличить. Информация об этом пути содержат метки вершин. В данном случае путь или увеличивающаяся цепочка 1246. Максимально возможный поток, который можно передать по дугам этого пути, определяется второй цифрой метки вершины стока, то есть 2. Поток в сети стал равным 2.
Вторая итерация. Первый шаг. Присвоим вершине 1 метку [1,@]. Рассмотрим дуги, началом которых является помеченная вершина 1. Это дуги (1,2) и (1,3). Вершина 2 не может быть помечена, так как пропускная способность дуги (1,2) исчерпана. Вершине 3 присваиваем метку [1,6]. Второй шаг. Выберем помеченную, но не просмотренную вершину. Это вершина 3. Повторяем действия. В результате вершина 4 получает метку [3,2]. Третий шаг. Выбираем вершину 4, только она помечена и не просмотрена. Вершине 6 присваиваем метку [4,1]. Почему только одна единица потока? На предыдущей итерации израсходованы две единицы пропускной способности данной дуги, осталась только одна. Вершина-сток достигнута. Найдена увеличивающая поток цепочка, это 1346, по которой можно ”протащить” единичный поток. Результирующий поток в сети равен 3.
Итак, в чем суть “техники меток” Форда и Фалкерсона? Первое. На каждой итерации вершины сети могут находиться в одном из трех состояний: вершине присвоена метка, и она просмотрена; вершине присвоена метка, и она не просмотрена, то есть не все смежные с ней вершины обработаны; вершина не имеет метки. Второе. На каждой итерации мы выбираем помеченную, но не просмотренную вершину v и пытаемся найти вершину u, смежную с v, которую можно пометить. Помеченные вершины образуют множество вершин сети G, достижимые из вершины-источника. Если среди этих вершин окажется вершина-сток, то это означает успешный результат поиска цепочки, увеличивающей поток, при неизменности этого множества работа заканчивается - поток изменить нельзя.
Алгоритм. Входные данные. Описание сети G=(V,E) матрицей пропускных способностей С[1..N,1..N], где N - количество вершин. Вершина-источник s и вершина-сток t. Выходные данные. Поток, описываемый матрицей F[1..N,1..N]. Рабочие переменные. Структура данных для хранения меток - P[1..N,1..2]. Элемент P[i,1] - номер вершины, из которой можно передать поток, равный P[i,2], в вершину с номером i. Логическая переменная Lg, значение true - есть цепочка, увеличивающая поток, false - нет.
Основная логика.
begin
<ввод данных и инициализация переменных(Lg:=true)>;
while Lg do begin
FillChar(P,SizeOf(P),0);
<процедура расстановки меток(Mark), если вершину t не смогли пометить, то Lg:=false; результат работы - значение P (метки вершин) >;
if Lg then <процедура Stream(t) - изменение потока по дугам найденной цепочки от вершины-стока t до вершины-источника s; входные данные - массив P, результат - измененный массив F>;
end;
<вывод потока F>;
end.{конец обработки}
Уточним логику расстановки меток (нелучший вариант).
procedure Mark;
var M:set of 1..N;i,l:integer;
begin
M:=[1..N]; {непросмотренные вершины}
P[s,1]:=s;P[s,2]:=maxint;{присвоим метку вершине-источнику}
l:=s;
while (P[t,1]=0) and Lg do begin
for i:=1 to N do {поиск непомеченной вершины}
if (P[i,1]=0) and ((C[l,i]<>0) or (C[i,l]<>0)) then
if F[l,i]<C[l,i] then begin{дуга прямая?}
P[i,1]:=l; if P[l,2]<C[l,i]-F[l,i] then P[i,2]:=P[l,2]
else P[i,2]:=C[l,i]-F[l,i];
end
else if F[i,l]>0 then begin{дуга обратная?}
P[i,1]:=-l;
if P[l,2]<F[i,l] then P[i,2]:=P[l,2] else P[i,2]:=F[i,l];
end;
M:=M-[l];{вершина с номером l просмотрена}
l:=1;{находим помеченную и непросмотренную вершину}
repeat Inc(l) until (l>N) or ((P[l,1]<>0) and (l in M));
if l>N then Lg:=false;
end;
end;
Логика изменения потока F имеет вид:
procedure Stream(q:integer);
begin
{определяем тип дуги - прямая или обратная, знак минус у номера вершины - признак обратной дуги}
if P[q,1]>0 then F[P[q,1],q]:=F[P[q,1],q]+P[t,2]
else F[q,abs(P[q,1])]:=F[q,abs(P[q,1])]-P[t,2];
{если не вершина-источник, то переход к предыдущей вершине цепочки}
if abs(P[q,1])<>s then begin
q:=abs(P[q,1]);Stream(q);end;
end;
Итак, рассмотрение метода построения максимального потока в сети завершено.