Задача
“Я хочу быть пиратом!” Мы напоминаем эту известную фразу Гайбраша Трипвуда из серии компьютерных игр Monkey Island («Остров Обезьян»). Гайбраш участвовал в другом приключении и серьезно нуждается в Вашей помощи, потому что на этот раз это вопрос жизни и смерти. Наш Гайбраш в последнем приключении приплыл на таинственный остров (ТО), чтобы найти подсказку для еще более таинственного сокровища. Тем временем Ли Чак узнал об этой поездке и подготовил ловушку Гайбрашу на ТО. ТО имеет прямоугольную форму (поскольку мы знаем, что он таинственный) и его карта может рассматриваться как матрица такой же размерности. Назовем каждый элемент матрицы участком. Некоторые участки могут быть заполнены горными скалами. Такие участки считаются непроходимыми.
Рассмотрим остров, карта которого изображена на рисунке. Эта карта представляет собой матрицу с $6$ строками и $7$ столбцами. Комнаты «R» показывают участки со скалами. Гайбраш должен начинать с участка, отмеченного «g», а Ли Чак – с участка «l». У Гайбраша есть шанс сбежать с этого проклятого острова, если он достигнет конечного участка, который отмечен символом «e» на карте. Каждую единицу времени Гайбраш может пойти на соседний с текущим участок по горизонтали или вертикали (но не по диагонали), если в нем нет скал, или не двигаться. То есть он может переместиться на один участок вверх, вниз, влево, вправо или вообще остаться на месте. В приведенном примере Гайбраш в первый момент времени может остаться или пойти в комнату слева от него. Все указанные правила применяются также и к движению Ли Чака, но с одним исключением: он не может войти на конечный участок (отмеченный «e»). То есть, каждую единицу времени Ли Чак может пойти на один участок вверх, вниз, влево, вправо (если только это не «R» или «e») или стоять. Мы предполагаем, что каждую единицу времени сначала делает ход (или стоит) Гайбраш, а затем ходит (или стоит) Ли Чак, в следующую единицу времени опять сначала Гайбраш, затем Ли Чак и так далее. Если Гайбраш и Ли Чак встретятся на одном участке, то Ли Чак немедленно убьет нашего бедного Гайбраша.
Ваша задача состоит в том, чтобы узнать, есть ли по крайней мере один безопасный путь или нет. Безопасный путь – это путь для Гайбраша (от «g» до «e») такой, что Ли Чак не может поймать Гайбраша на этом пути независимо от того, что он (Ли Чак) делает каждую единицу времени.
Входные данные
Первая строка входа содержит единственное целое число — количество тестовых случаев. Далее идут строки данных для тестовых случаев. Каждый тест начинается со строки, содержащей два целых числа $R$ и $C$ ($4 \leq R, C \leq 30$), которые обозначают количество строк и столбцов карты таинственного острова соответственно. Далее следуют $R$ строк, каждая содержит $C$ символов, представляющих карту. Есть единственные отметки «g», «l» и «e» на карте.
Выходные данные
Для каждого теста необходимо вывести единственную строку. Если существует, по крайней мере, хотя бы один безопасный путь для тестового случая, должно быть выведено слово «YES», и слово «NO», если такого пути нет. Предполагается, что если существует безопасный путь, то необходимо не более $1000$ единиц времени для прохождения по нему Гайбраша.
Тесты
Входные данные | Выходные данные |
$531$ $348$ $1645$ | $911$ |
$1784353$ $453345$ $463973$ | $214457$ |
$39252362$ $345673$ $786536$ | $302328$ |
$68790234$ $679643$ $789057$ | $281232$ |
$324$ $8564$ $45074547$ | $32984424$ |
Код программы
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 |
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.LinkedList; import java.util.Queue; public class Main{ private static class Node { private int i; private int j; private Node(int i, int j) { this.i = i; this.j = j; } } private static void fill(int[][] a, int value) { for (int i = 0; i < a.length; i++) { for (int j = 0; j < a[i].length; j++) { a[i][j] = value; } } } private static void bfsSecondPlayer(char[][] map, int[][] moves, Node start) { Queue<Node> queue = new LinkedList<>(); moves[start.i][start.j] = 0; queue.add(start); while (!queue.isEmpty()) { Node curNode = queue.poll(); int i = curNode.i; int j = curNode.j; if (i > 0 && map[i - 1][j] != 'R' && map[i - 1][j] != 'e' && moves[i][j] + 1 < moves[i - 1][j]) { moves[i - 1][j] = moves[i][j] + 1; queue.add(new Node(i - 1, j)); } if (i < moves.length - 1 && map[i + 1][j] != 'R' && map[i + 1][j] != 'e' && moves[i][j] + 1 < moves[i + 1][j]) { moves[i + 1][j] = moves[i][j] + 1; queue.add(new Node(i + 1, j)); } if (j > 0 && map[i][j - 1] != 'R' && map[i][j - 1] != 'e' && moves[i][j] + 1 < moves[i][j - 1]) { moves[i][j - 1] = moves[i][j] + 1; queue.add(new Node(i, j - 1)); } if (i < moves[0].length - 1 && map[i][j + 1] != 'R' && map[i][j + 1] != 'e' && moves[i][j] + 1 < moves[i][j + 1]) { moves[i][j + 1] = moves[i][j] + 1; queue.add(new Node(i, j + 1)); } } } private static boolean bfsFirstPlayer(char[][] map, int[][] moves, int[][] secondPlayerMoves, Node start) { Queue<Node> queue = new LinkedList<>(); queue.add(start); moves[start.i][start.j] = 0; while (!queue.isEmpty()) { Node curNode = queue.poll(); int i = curNode.i; int j = curNode.j; if (map[i][j] == 'e') { return true; } if (i > 0 && map[i - 1][j] != 'R' && moves[i][j] + 1 < Math.min(moves[i - 1][j], secondPlayerMoves[i - 1][j])) { moves[i - 1][j] = moves[i][j] + 1; queue.add(new Node(i - 1, j)); } if (i < moves.length - 1 && map[i + 1][j] != 'R' && moves[i][j] + 1 < Math.min(moves[i + 1][j], secondPlayerMoves[i + 1][j])) { moves[i + 1][j] = moves[i][j] + 1; queue.add(new Node(i + 1, j)); } if (j > 0 && map[i][j - 1] != 'R' && moves[i][j] + 1 < Math.min(moves[i][j - 1], secondPlayerMoves[i][j - 1])) { moves[i][j - 1] = moves[i][j] + 1; queue.add(new Node(i, j - 1)); } if (i < moves[0].length - 1 && map[i][j + 1] != 'R' && moves[i][j] + 1 < Math.min(moves[i][j + 1], secondPlayerMoves[i][j + 1])) { moves[i][j + 1] = moves[i][j] + 1; queue.add(new Node(i, j + 1)); } } return false; } public static void main(String[] args) throws IOException{ BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in)); int tests = Integer.parseInt(bufferedReader.readLine()); for (int r = 0; r < tests; r++) { String[] sizes = bufferedReader.readLine().split(" "); int n = Integer.parseInt(sizes[0]); int m = Integer.parseInt(sizes[1]); char[][] map = new char[n][m]; Node g = null; Node l = null; Node e = null; for (int i = 0; i < n; i++) { map[i] = bufferedReader.readLine().toCharArray(); for (int j = 0; j < m; j++) { if (map[i][j] == 'g') { g = new Node(i, j); } else if (map[i][j] == 'l') { l = new Node(i, j); } else if (map[i][j] == 'e') { e = new Node(i, j); } } } int[][] secondPlayerDistances = new int[n][m]; fill(secondPlayerDistances, Integer.MAX_VALUE); bfsSecondPlayer(map, secondPlayerDistances, l); int[][] firstPlayerDistances = new int[n][m]; fill(firstPlayerDistances, Integer.MAX_VALUE); boolean ok = bfsFirstPlayer(map, firstPlayerDistances, secondPlayerDistances, g); System.out.println(ok ? "YES" : "NO"); } } } |
Решение задачи
Представим карту острова в виде неориентированного графа, вершинами которого в случае Гайбраша являются все участки, кроме участков с пометкой «R», а для Ли Чака — все участки, кроме участков с пометками «R» и «e». Две вершины будут соединяться ребром, если они соответствуют участкам, имеющим общую сторону. Обозначим начальное местоположение Гайбраша — $g,$ Ли Чака — $l.$, выход $e.$
Безопасный для Гайбраша маршрут существует тогда и только тогда, когда существует путь $\omega,$ такой, что для $\forall v \in \omega \ \rho \left(g, v \right ) + 1 < \rho(l, v).$ С помощью поиска в ширину найдем минимальное количество шагов, за которое Ли Чак попадает в каждую клетку, в которую он может попасть. Аналогично реализуем поиск в ширину для Гайбраша с той лишь разницей, что Гайбраш должен миновать те вершины графа, в которые он будет добираться дольше, чем Ли Чак. Если при этом найдется путь, соединяющий вершину, соответствующую начальному местоположению Гайбраша с вершиной, соответствующую цели, то он сможет спастись, в противном случае — нет.
Ссылки
Условие задачи на e-olymp
Решение на e-olymp
Код решения на Ideone