Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Алгоритмы на графах, Окулов.doc
Скачиваний:
54
Добавлен:
14.11.2018
Размер:
4.15 Mб
Скачать

3.10.2. Алгоритм Эйлера

Этот алгоритм и следующий работоспособны в том случае, если выполняется неравенство треугольника. Его суть в том, что для любой тройки городов i, j, k (между которыми есть связь) выполняется неравенство di,j+dj,k>di,k. Рассмотрим идею алгоритма.

Шаг1. Строится каркас минимального веса (алгоритмы Прима или Краскала).

Примечание. Это не есть первое приближение, как в предыдущем алгоритме.

Шаг 2. Путем дублирования каждого ребра каркас преобразуется в эйлеров граф.

Шаг 3. Находим в построенном графе эйлеров цикл.

Шаг 4. Эйлеров цикл преобразуем в гамильтонов цикл (или маршрут коммивояжера). Метод преобразования: последовательность вершин эйлерова цикла сокращается так, чтобы каждая вершина графа в получившемся цикле встречалась ровно один раз.

Шаг 5. Закончить работу алгоритма. Получено приближенное решение задачи коммивояжера.

Покажем , что стоимость приближенного решение CostAp не превосходит удвоенной стоимости оптимального решения CostBet. Пусть стоимость каркаса - CostFr. Тогда CostFr<CostBet, так как при удалении из оптимального пути коммивояжера ребра получаем каркас с весом не большим, чем CostAp. Из правила построения эйлерова графа получаем, что вес построенного эйлерова цикла 2*СostFr. Неравенство треугольника обеспечивает результат: следующую оценку шага 4 - CostAp<2*CostFr, а значит, CostAp<2*CostBet.

Рассмотрим пример.

Для исходных данных, приведенных в п. 2.1.7, на рисунке жирными линиями выделен каркас минимального веса. Тонкие линии добавляются на шаге 2. Получаем эйлеров граф. Затем эйлеров цикл (маршрут) преобразуется в путь коммивояжера (шаг 4). Согласно неравенству треугольника мы получаем: d2,9+d9,8>d2,8; d2,8+d8,1>d2,1; d2,1+d1,7>d2,7. Суммируя левые и правые части неравенств, получаем: d2,9+d9,8+d8,1+d1,7>d2,7. Для рассмотренного примера CostAp равна 202. Это ~ 1.28*CostBet.

Структуры данных. Помимо названных ранее массивов А - матрица расстояний и массива Way - хранение искомого пути, требуется «что-то» для хранение описания каркаса. Пусть это будет массив B (B:array[1..Nmax,1..Nmax] of boolean). Элемент B[i,j], равный true, говорит о том, что ребро (i,j) графа принадлежит каркасу.

Общая логика.

Begin

init;{ввод описания - матрица расстояний; инициализация данных}

solve;{решатель}

out;{вывод результата}

end.

Процедуры init и out очевидны (должны писаться «на автомате»). Уточняем процедуру solve. Первый «набросок».

Procedure solve;

var ?

<процедуры и функции>;

begin

init_solve;{инициализация переменных процедуры solve}

find_tree;{построение каркаса}

eiler_way;{поиск эйлерова цикла}

komm_way;{поиск пути коммивояжера}

end;

Прежде чем продолжать уточнение, необходимо определить структуры данных этой части логики и взаимодействие ее составных частей. Во-первых, при построении каркаса необходимо иметь список ребер, отсортированный в порядке возрастания их весов (алгоритмы Краскала и Прима). Следовательно, на входе процедуры find_tree матрица расстояний A, на выходе - B, рабочие структуры - массив для хранения списка ребер. Внутренние процедуры: формирование списка ребер и сортировки. Продолжим рассмотрение. Эйлеров цикл необходимо где-то хранить, пусть это будет массив St (St:array[1..n*(n-1) div 2] of byte. Количество не нулевых элементов в St - значение переменной Count. Эти величины описываются в разделе переменных процедуры solve, их инициализация - в процедуре init_solve.Процедуру поиска эйлерова цикла сделаем рекурсивной, поэтому первый вызов изменится - eiler_way(1). Выбор начальной вершины при поиске эйлерова цикла не имеет значения.

Итак, классическая логика поиска эйлерова цикла. Приводится с целью показа работы процедуры komm_way, ибо последняя не есть поиск гамильтонова цикла в обычной трактовке.

procedure eiler_way(v:byte);

var j:integer;

begin

for j:=1 to N do

if b[v,j] then begin b[v,j]:=false;b[j,v]:=false;eiler_way(j);end;

inc(count);St[count]:=v;{заносим номер вершины в эйлеров цикл}

end;

procedure komm_way;

var s:set of 1..Nmax;

i,j:integer;

begin

i:=0;s:=[];

for j:=1 to Count do{исключаем повторяющиеся номера вершин из эйлерова цикла}

if Not(St[j] in s) then begin

inc(i);way[i]:=St[j];s:=s+[St[j]];

end;

end;