1064. Путь коня

 

Дана шахматная доска, состоящая из n×n клеток, несколько из них вырезано. Провести ходом коня через невырезанные клетки путь минимальной длины из одной клетки в другую.

 

Вход. В первой строке задано число n (2 ≤ n ≤ 50). В следующих n строках содержится по n символов. Символом # обозначена вырезанная клетка, точкой – невырезанная клетка, символом @ – начальная и конечная клетки пути коня (таких символов два).

 

Выход. Если путь построить невозможно, то вывести “Impossible”. В противном случае вывести такую же карту, как и на входе, но пометить все промежуточные положения коня символом @.

 

Пример входа

Пример выхода

5
@..@.
..##.
.....
.....

.....

@..@.

..##.

.@..@

..@..

@....

 

 

РЕШЕНИЕ

графы – поиск в ширину

 

Анализ алгоритма

Для решения задачи достаточно запустить поиск в ширину из одной из клеток, обозначенной символом @. Потом при помощи массива предков следует восстановить кратчайший путь, если он существует.

 

Реализация алгоритма

В массивах dx и dy описываем все возможные ходы коня. Из клетки (i, j) конь может пойти в одну из клеток (i + dx[k], j + dy[k]), 0 ≤ k ≤ 7.

 

int dx[] = {1,1,-1,-1,2,2,-2,-2};

int dy[] = {2,-2,2,-2,1,-1,1,-1};

 

Позицию коня на доске будем хранить парой координат (x, y), для этого объявим структуру CELL. Объявим операторы сравнения ячеек (“равно” и “не равно”).

 

struct CELL

{

  int x, y;

  CELL(int a = -1, int b = -1) : x(a), y(b) {};

};

 

int operator==(CELL a, CELL b)

{

  return (a.x == b.x) && (a.y == b.y);

}

 

int operator!=(CELL a, CELL b)

{

  return !((a.x == b.x) && (a.y == b.y));

}

 

Объявим массив предков Parent. Массив предков также будем использовать для запоминания посещенных клеток шахматной доски. Если Parent[i][j] = (-1, -1), то ячейка (i, j) еще не посещалась конем. Определим пустую ячейку EMPTY_CELL.

 

CELL EMPTY_CELL(-1,-1), Start(-1,-1), Finish(-1,-1);

CELL Parent[MAX][MAX];

 

Начальное состояние доски считываем в массив s.

 

char s[MAX][MAX];

 

Функция CanGo возвращает 1, если ячейка c не находится за границей поля.

 

int CanGo(CELL c)

{

  return !((c.x < 0) || (c.x >= n) || (c.y < 0) || (c.y >= n));

}

 

Функция bfs запускает поиск в ширину из ячейки Start. Объявим очередь d.

 

void bfs(CELL Start)

{

  deque<CELL> d;

  d.push_back(Start);

  s[Start.x][Start.y] = '#';

  while(!d.empty())

  {

 

Берем из головы очереди позицию типа Node. Перебираем все возможные ходы коня из нее.

 

    CELL Node = d.front(); d.pop_front();

    for(int k = 0; k < 8; k++)

    {

      CELL Next = CELL(Node.x + dx[k], Node.y + dy[k]);

 

Если ход совершается в позицию Next, которая находится за краями доски, то совершить его невозможно.

 

      if (!CanGo(Next)) continue;

 

Если позиция Next представляет собой невырезанную клетку, и мы там еще не были, то идем туда. В массиве предков отмечаем, что в Next мы попали из вершины Node.

 

      if ((s[Next.x][Next.y] != '#') &&

          (Parent[Next.x][Next.y] == EMPTY_CELL))

      {

        Parent[Next.x][Next.y] = Node;

 

Если мы попали в конечную вершину Finish, то завершаем поиск.

 

        if (Next == Finish) return;

 

Заносим клетку Next в конец очереди d.

 

        d.push_back(Next);

      }

    }

  }

}

 

Основная часть программы. Проводим инициализацию переменных. Читаем состояние шахматной доски в массив s. Координаты начального и конечного положения коня, отмеченные на доске символом @, заносим в переменные Start и Finish типа CELL.

 

scanf("%d\n",&n);

for(i = 0; i < n; i++) gets(s[i]);

for(i = 0; i < n; i++)

for(j = 0; j < n; j++)

  if (s[i][j] == '@')

  {

    if (Start.x == -1) {Start.x = i; Start.y = j;}

    else {Finish.x = i; Finish.y = j;}

  }

 

Запускаем поиск в ширину из клетки Start.

 

bfs(Start);

 

Поиск в ширину мы могли завершить по выполнению одного из двух условий: либо просмотрены все вершины, куда можно попасть конем из клетки Start и очередь d стала пустой, либо в какой-то момент времени мы попали в Finish и вышли из функции bfs.

Если поиск в глубину не добрался до ячейки Finish, то искомого пути коня не существует.

 

if (Parent[Finish.x][Finish.y] == EMPTY_CELL) printf("Impossible\n");

else

{

 

Если кратчайший путь из Start в Finish существует, то при помощи массива предков восстанавливаем его. Кратчайший путь коня заполняем символами @.

 

  s[Start.x][Start.y] = '@';

  while(Start != Finish)

  {

    s[Finish.x][Finish.y] = '@';

    Finish = Parent[Finish.x][Finish.y];

  }

 

Выводим результирующую шахматную доску.

 

  for(i = 0; i < n; i++) puts(s[i]);

}