Метод Дейкстра, применяется для
*поиска кратчайших расстояний от одной вершины до всех остальных
*построения оптимальных маршрутов от одной вершины до всех остальных

Задача "Пирамида Хеопса" (Автор - Котов В.М.)
Республиканская олимпиада по информатике 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.