Метод Дейкстра, применяется для
*поиска кратчайших расстояний от одной вершины до всех остальных
*построения оптимальных маршрутов от одной вершины до всех остальных
Задача "Пирамида Хеопса" (Автор - Котов В.М.)
Республиканская олимпиада по информатике 1996г
Внутри пирамиды Хеопса есть N комнат, в которых установлены 2М модулей, составляющих М устройств. каждое устройство состоит из двух модулей, которые располагаются в разных комнатах, и предназначено для перемещения между парой комнат, в которых установлены эти модули. Перемещение происходит за 0.5 условных единицы времени. В начальный момент времени модули всех устройств переходят в "подготовительный режим". Каждый из модулей имеет некоторый свой целочисленный период времени, в течение которого он находится в "подготовительном режиме". По истечении этого времени модуль мгоновенно "срабатывает" после чего опять переходит в "подготовительный режим". Устройством можно воспользоваться только в тот момент, когда одновременно "срабатывают" оба его модуля.
Индиана Джонс сумел проникнуть в гробницу фараона (комната 1). Обследовав ее, он включил устройства и собрался уходить, но в этот момент проснулся хранитель гробницы. Теперь Джонсу необходимо как можно быстрее попасть в комнату N, в которой находится выход из пирамиды. При этом из комнаты в комнату он может попадать только при помощи устройств, так как проснувшийся хранитель закрыл все двери в комнатах пирамиды.
Необходимо написать программу, которая получает на входе описания расположения устройств и их характеристик (смотри описание формата ввода), а выдает значение оптимального времени и последовательность устройств, которыми надо воспользоваться, что бы попасть из комнаты 1 в комнату N за это время.
Входной файл: input2.txt Выходной файл: output2.txt
Формат ввода:
N
M
R11 T11 R12 T12
...
RM1 TM1 RM2 TM2
где
N (2<=N<=100) - количество комнат
M (M<=100) - количество устройств
Ri1 и Ri2 - номера комнат, в которых располагаются модули устройства i
Ti1, Ti2 (Ti1,Ti2<=1000) - периоды времени, через которые срабатывают эти модули
Все числа - натуральные.
Пример
4
5
1 5 3 2
1 1 2 1
2 5 3 5
4 4 3 2
3 5 4 5
Оптимальное время: 8.5
искомая последовательность: 2 3 4
Кратко алгоритм решения может быть изложен следующим образом.
Представив комнаты - вершинами, а пары устройств (с временами срабатывания) - взвешенными дугами,
можем применить алгоритм Дейкстра для поиска кратчайшего пути от первой вершины до последней. Точнее, алгоритм Дейкстра построит кратчайшие пути от первой до всех остальных, но в данной задаче нас интересует только кратчайший путь от первой до последней.
Нужно учитывать, что дуги существуют только в моменты, кратные временам срабатывания.
Рассмотрим решение более подробно.
Ввод исходных данных :
read(N,M); for I:=1 to N do kG[i]:=0;
for i:=1 to M do
begin
read(r1,t1,r2,t2);
nok:= (t1*t2) div Nod(t1,t2); {nok - наименьшее общее кратное}
{Nod-наибольший общий делитель }
{ чисел t1 и t2 }
inc(kG[r1]); G[r1,kg[r1]]:=r2; P[r1,kg[r1]]:=nok;
inc(kG[r2]); G[r2,kg[r2]]:=r1; P[r2,kg[r2]]:=nok;
end;Исходный граф по условиям задачи неориентированный. Поэтому мы вводим обе дуги. При этом граф представляется в виде списка дуг смежных с текущей: kG[i] - количество дуг из вершины i
G[i,j] - вершина, в которую из вершины i идет дуга под порядковым номером j
P[i,j] - вес дуги из вершины i вершину G[i,j]
Для вычисления nod используется функция, основанная на алгоритме Евклида:
function Nod (a,b:longint):longint;
begin
while (a<>b) do if a>b then a:=a-b else b:=b-a;
Nod:=a;
end;Затем проводится подготовка к выполнению алгоритма Дейкстры:
for i:=1 to N do { Для всех вершин }
begin
Labl[i]:=FREE; { Помечаем как свободную }
pred[i]:=1; { Предок - первая }
d[i]:=maxlongint; { Расстояние до первой - маx }
end;
Labl[1]:=DONE; { Первая - обработана }
Pred[1]:=0; { Предок у первой - 0 }
d[1]:=0; { Расстояние от первой до первой - 0 }
for j:=1 to kG[1] do { Для всех дуг из первого ребра }
d[G[1,j]]:=P[1,j]+0.5; { Расстояние до первой вершины }
{ равно весу ребра + 0.5 }Заметим, что в последней строке к весу ребер добавляется 0.5, поскольку по условиям задачи перемещение с помощью устройств из одной комнаты в другую занимает 0.5 единицы времени.
Далее идет основной цикл алгоритма Дейкстра
for i:=1 to N-1 do
begin
Поиск ближайшей к первой вершине из необработанных
Пересчет кратчайших расстояний через ближайшую вершину
до вершин, смежных с ближайшей
end;Первый этап - "Поиск ближайшей к первой вершине из необработанных"
MinD:=maxlongint; { MinD - max }
for j:=1 to N do { Для всех вершин }
if (Labl[j]=FREE) { если вершина свободна }
and (d[j]<MinD) { и расстояние от нее до первой }
{ вершины меньше MinD }
then
begin
MinD:=d[j]; { Заносим это расстояние в MinD }
Bliz:=j { Вершину запоминаем как ближайшую }
end;
Labl[Bliz]:=DONE; { Найденную ближайшую помечаем }
{ как обработанную }Второй этап - "Пересчет кратчайших расстояний через ближайшую вершину до вершин, смежных с ближайшей"
for j:=1 to kG[Bliz] do { Для всех вершин смежных с ближайшей}
if (Labl[G[Bliz,j]]=FREE) {Если вершина еще не обрабатывалась }
then
begin
NewTime:=Calculat(d[bliz],P[bliz,j]); {Вычислитьрасстояниедонее}
{в терминах задачи-время }
{перемешения в нее }
if d[G[Bliz,j]]> NewTime +0.5 {Если новое время лучше }
then
begin
d[G[Bliz,j]]:= NewTime+0.5; {заносим его в массив D }
pred[G[Bliz,j]]:=Bliz; {указываем ближайшую как }
end; {предыдущую для текущей }
end;При вычислении кратчайшего расстояния до текущей вершины через ближайшую (в терминах алгоритма Дейкстра) или времени перемещения из первой комнаты через ближашую в текущую (в терминах данной задачи)
используется функция Calculat (T,Tnok):
function Calculat (T:single; Tnok:longint):longint;
var i : longint;
begin
TRes:=0;
while (T>TRes) do inc(TRes,Tnok);
Calculat:=TRes ;
end;Эта функция вычисляет время TRes (которое передается в вызывающую программу непосредственно через имя функции Calculat), ближайшее большее текущего времени T (T равно минимальному значению времени, которое потребовалось, что бы оптимальным образом добраться из первой вершины до ближайшей вершины) и кратное Tnok - периоду срабатывания пары устройств, связывающих комнаты Bliz (ближайшая) и G[Bliz,j] (текущая).
После выполнения алгоритма Дейкстра сформированы массивы
d[j] - кратчайшее расстояние от начальной вершины до вершины j
pred[j] - предок вершины j на оптимальном маршруте
По этой информации вывод результата осуществляется следующим образом:
tg:=N; i:=0; { Начиная с последней вершины }
while tg<>0 do { Пока не закончились предшествующие }
begin
inc(i); { Инкрементируем количество вершин в пути }
path[i]:=tg; { Заносим текущую в массив пути }
tg:=pred[tg]; { Переустанавливаем предыдущую }
end;
writeln('Оптимальное время: ',D[N]:0:1);
write('искомая последовательность:');
for j:=i-1 downto 1 do write(' ',path[j]);Ниже приведен полный текст решения задачи.
{$R+,E+,N+}
program by96d1s2;
const
FREE=1;
DONE=2;
var
G : array[1..100,1..100] of byte;
P : array[1..100,1..100] of longint;
D : array[1..100] of single;
Labl,pred,kG : array[1..100] of byte;
path : array[1..100] of byte;
is,t1,t2,nok,Tres,NewTime : longint;
i,j,x,y,A,B,C,N,M,tg,Bliz : byte;
r1,r2 : byte;
Yes : boolean;
MinD : single;
function Nod (a,b:longint):longint;
begin
while (a<>b) do
if a>b then a:=a-b else b:=b-a;
Nod:=a;
end;
function Calculat (T:single; Tnok:longint):longint;
var i : longint;
begin
TRes:=0;
while (T>TRes) do inc(Tres,Tnok);
Calculat:=TRes ;
end;
begin
assign(input,'input2.txt'); reset(input);
assign(output,'output2.txt'); rewrite(output);
read(N,M);
for I:=1 to N do kG[i]:=0;
for i:=1 to M do
begin
read(r1,t1,r2,t2); nok:= (t1*t2) div Nod(t1,t2);
inc(kG[r1]); G[r1,kg[r1]]:=r2; P[r1,kg[r1]]:=nok;
inc(kG[r2]); G[r2,kg[r2]]:=r1; P[r2,kg[r2]]:=nok;
end;
for i:=1 to N do
begin
Labl[i]:=FREE; pred[i]:=1; d[i]:=maxlongint;
end;
Labl[1]:=DONE; Pred[1]:=0; d[1]:=0;
for j:=1 to kG[1] do d[G[1,j]]:=P[1,j]+0.5;
for i:=1 to N-1 do
begin
MinD:=maxlongint;
for j:=1 to N do
if (Labl[j]=FREE) and (d[j]<MinD)
then begin MinD:=d[j]; Bliz:=j end;
Labl[Bliz]:=DONE;
for j:=1 to kG[Bliz] do
if (Labl[G[Bliz,j]]=FREE)
then begin
NewTime:=Calculat(d[bliz],P[bliz,j]);
if d[G[Bliz,j]]> NewTime +0.5
then
begin
d[G[Bliz,j]]:= NewTime+0.5;
pred[G[Bliz,j]]:=Bliz;
end;
end;
end;
tg:=N; i:=0;
while tg<>0 do
begin
inc(i); path[i]:=tg; tg:=pred[tg];
end;
writeln('Оптимальное время: ',D[N]:0:1);
write('искомая последовательность:');
for j:=i-1 downto 1 do write(' ',path[j]);
close(input); close(output);
end.