Подразделы

Другие разделы

Дата и время

19/04/2024 20:04:34

Авторизация

Имя:
Пароль:
Зарегистрироваться
Восстановить пароль
 

printРазбор задачи B. Великий пожар Анк-Морпорка

Тема: поиск в ширину
Сложность: средняя

Для решения задачи необходимо использовать известный алгоритм поиска в ширину. Для лучшего понимания алгоритма рекомендуется рассмотреть разбор задач 741. Ход конем и 1509. Очистка озера.

Дополнительно можно воспользоваться следующими оптимизациями: 1) окружить поле клетками с '.'; 2) свести задачу к одному измерению (т.е. текущее состояние будет описываться одним целым числом).
Объявление необходимых структур данных:
var n,m:integer; { размеры карты }
  map:array [0..52*52] of char; { карта }
  q:array [0..50*50] of integer; { очередь } 
  q1,q2:integer; { индекс начала и конца очереди }
  step:array [0..52*52] of integer; { информация о времени возгорания, -1 означает, что дом не горел }
  maxkol,maxr,maxc, { наилучший результат и координаты дома для поджога }
  curstep,newstep, { текущее время и время возгорания следующего дома }
  qpos0,qpos1,qpos2, { начальные позиции в очереди для предыдущего, текущего и нового времени возгорания }
  i,j,p,pn:integer; {вспомогательные переменные }
Первая часть программы – ввод данных. Двумерная карта города размером `(n+2)\ times\ (m+2)` хранится в одномерном массиве последовательно по строкам. Ячейка с координатами `(i,j)` в одномерном массиве имеет индекс `i*(m+2)+j`. Строки карты с номерами 0 и `n+1` и столбцы с номерами 0 и `m+1` заполняются символами '.'.
  readln(n,m);
  for j:=0 to m+1 do
    map[j]:='.';
  for i:=1 to n do
  begin
    map[i*(m+2)]:='.';
    for j:=1 to m do
      read(map[i*(m+2)+j]);
    map[i*(m+2)+m+1]:='.';
    readln;
  end;
  for j:=0 to m+1 do
    map[(n+1)*(m+2)+j]:='.';
Далее перебираем все варианты, какой дом будет подожжен первым, и выполняем поиск в ширину. При изменении времени возгорания смещаем позиции указателей qpos0, qpos1, qpos2 в очереди и проверяем на увеличение текущего максимума одновременно горящих домов.
  maxkol:=0;
  for i:=1 to n do
    for j:=1 to m do
    begin
      p:=i*(m+2)+j; { выбираем клетку (i,j) }
      if map[p]='#' then { если там есть дом }
      begin
        for pn:=0 to (n+2)*(m+2)-1 do { очищаем массив времени возгорания }
          step[pn]:=-1;
        step[p]:=0; { дом в клетке (i,j) загорается в момент 0 }
        q[0]:=p; { помещаем его в очередь }
        q1:=0;q2:=1;
        curstep:=0;
        qpos0:=0;
        qpos1:=0;
        qpos2:=0;
        while q1<q2 do { очередь не пуста }
        begin
          p:=q[q1];
          inc(q1);
          newstep:=step[p]+1; { новое время }
          if newstep>curstep then { время изменилось }
          begin
            qpos0:=qpos1; {  сдвигаем указатели }
            qpos1:=qpos2;
            qpos2:=q2;
            curstep:=newstep;
            if qpos2-qpos0>maxkol then { найден новый максимум }
            begin
              maxkol:=qpos2-qpos0; { запоминаем }
              maxr:=i;
              maxc:=j;
            end;
          end;
          fire(p+1); { поджигаем все соседние дома }
          fire(p-1);
          fire(p+m+1);
          fire(p+m+2);
          fire(p+m+3);
          fire(p-m-1);
          fire(p-m-2);
          fire(p-m-3);
        end;
      end;
    end;
Подпрограмма fire выглядит следующим образом:
procedure fire(p:integer);
begin
  if (map[p]='#') and (step[p]=-1) then { в позиции p есть дом, который еще не горел }
  begin
    step[p]:=newstep; { отмечаем время возгорания }
    q[q2]:=p; { добавляем дом в очередь }
    inc(q2);
  end;
end;
В завершающей части программ выводится найденный максимум и координаты дома.
  writeln(maxkol);
  writeln(maxr,' ',maxc);
Другим способом ускорения работы своей программы является выключение проверок на выход за границы массива и переполнение, включенных по умолчанию при компиляции – см. опции компилятора на странице Помощь. Для этого в начале программы нужно дописать комментарий:
{$R-} {$Q-} {$S-}
Следует отметить, что на первоначальных этапах разработки эти проверки являются достаточно важными и дают дополнительную информацию для исправления ошибок в программе. Ошибка времени исполнения (RT) часто сигнализирует о неправильно выбранном диапазоне для цикла или неправильно выбранном типе данных (integer вместо longint или int64). При выключении проверок ошибка типа RT превращается в WA или PE, которые обычно сигнализируют о нерассмотренном случае или неверной формуле.
Рекомендуется выключать проверки только при получении TL, и такое выключение сможет помочь только в случае, если реализованный алгоритм решения задачи требует существенно меньше `10^9` операций. Иначе нужно изменять алгоритм.
loading