Skip to Content

Solve the Maze

Análisis oficial (C++) 

Explicación

La observación principal es que si hay una persona mala junto a una persona buena, entonces es imposible. Por ejemplo, consideremos lo siguiente:

##### ##BG# ###.. ####.

Como la persona buena es adyacente a la persona mala, la persona mala puede simplemente moverse a la derecha y ahora tiene acceso completo a dondequiera que pueda moverse la persona buena. No importa si la persona buena puede llegar, porque la mala también puede. Además, no está permitido reemplazar a una persona buena por una pared, así que no está permitido impedir el movimiento de la persona mala.

Con esta observación, la solución es relativamente simple. Comprobamos esta adyacencia y, si existe, imprimimos “No.” En caso contrario, procedemos rodeando a las personas malas con paredes. Hacemos flood fill (relleno) desde el punto final (N1, M1)(N - 1,\space M - 1), y nos aseguramos de que todas las personas buenas hayan sido visitadas. No hace falta comprobar que las personas malas no llegaron, porque rodearlas con paredes es suficiente.

Implementación

Complejidad temporal: O(TNM)\mathcal{O}(TNM)

#include <bits/stdc++.h> using namespace std; typedef long long ll; const int mxN = 55; char grid[mxN][mxN]; bool visited[mxN][mxN]; int rowMovement[4]{0, 1, 0, -1}; int columnMovement[4]{1, 0, -1, 0}; int N, M; void floodfill(int r, int c) { if (r < 0 || r >= N || c < 0 || c >= M) return; if (grid[r][c] == '#' || visited[r][c]) return; visited[r][c] = true; floodfill(r + 1, c); floodfill(r - 1, c); floodfill(r, c + 1); floodfill(r, c - 1); } void solve() { memset(grid, '.', sizeof(grid)); memset(visited, 0, sizeof(visited)); cin >> N >> M; for (int i = 0; i < N; i++) { for (int j = 0; j < M; j++) { cin >> grid[i][j]; } } // rodear a las personas malas bool ok = true; for (int i = 0; i < N; i++) { for (int j = 0; j < M; j++) { if (grid[i][j] == 'B') { for (int x = 0; x < 4; x++) // recorrer las 4 direcciones { int newRow = i + rowMovement[x]; int newColumn = j + columnMovement[x]; // comprobar si está dentro de los límites if (newRow >= 0 && newRow < N && newColumn >= 0 && newColumn < M) { if (grid[newRow][newColumn] == 'G') { cout << "No\n"; return; } if (grid[newRow][newColumn] == '.') { grid[newRow][newColumn] = '#'; // convertirlo en # } } } } } } floodfill(N - 1, M - 1); for (int i = 0; i < N; i++) { for (int j = 0; j < M; j++) { if (grid[i][j] == 'G' && !visited[i][j]) { cout << "No\n"; return; } } } cout << "Yes\n"; } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int T; cin >> T; while (T--) { solve(); } }
import java.io.*; import java.util.*; public class SolveTheMaze { static int[] rowMovement = {0, 1, 0, -1}; // derecha, abajo, izquierda, arriba static int[] columnMovement = {1, 0, -1, 0}; static char[][] grid; static boolean[][] visited; static int numRows; static int numColumns; static boolean inBoundaries(int newRow, int newColumn) { if (newRow >= 0 && newRow < numRows) { if (newColumn >= 0 && newColumn < numColumns) { return true; } } return false; } static void floodfill(int row, int column) { visited[row][column] = true; for (int x = 0; x < 4; x++) { // las 4 direcciones int newRow = row + rowMovement[x]; int newColumn = column + columnMovement[x]; if (inBoundaries(newRow, newColumn)) { // dentro de los límites // no visitado, y no # if (!visited[newRow][newColumn]) { if (grid[newRow][newColumn] != '#') { floodfill(newRow, newColumn); } } } } } static String solve() { // rodear a los malos for (int i = 0; i < numRows; i++) { for (int j = 0; j < numColumns; j++) { if (grid[i][j] == 'B') { for (int x = 0; x < 4; x++) { // las 4 direcciones int newRow = i + rowMovement[x]; int newColumn = j + columnMovement[x]; // comprobar si está dentro de los límites if (inBoundaries(newRow, newColumn)) { if (grid[newRow][newColumn] == 'G') { return "No"; } if (grid[newRow][newColumn] == '.') { // convertirlo en # grid[newRow][newColumn] = '#'; } } } } } } if (grid[numRows - 1][numColumns - 1] != '#') { // hacer flood fill floodfill(numRows - 1, numColumns - 1); } for (int i = 0; i < numRows; i++) { for (int j = 0; j < numColumns; j++) { // devolver falso si encontramos una G no visitada if (grid[i][j] == 'G' && !visited[i][j]) { return "No"; } } } return "Yes"; } public static void main(String[] args) { Kattio io = new Kattio(); int numTestCases = io.nextInt(); for (int x = 0; x < numTestCases; x++) { numRows = io.nextInt(); numColumns = io.nextInt(); grid = new char[numRows][numColumns]; visited = new boolean[numRows][numColumns]; for (int row = 0; row < numRows; row++) { // leer la grilla String line = io.next(); for (int column = 0; column < numColumns; column++) { grid[row][column] = line.charAt(column); } } io.println(solve()); } io.close(); } // CodeSnip{Kattio} }
for _ in range(int(input())): n, m = map(int, input().split()) # Guarda las coordenadas de todas las personas buenas, que iremos quitando a medida que las protegemos. good_people = set() grid = [] for r in range(n): grid.append(list(input().strip())) for c, char in enumerate(grid[-1]): if char == "G": good_people.add((r, c)) visited = [[False] * len(grid[0]) for _ in range(len(grid))] stack = [(n - 1, m - 1)] while stack: i, j = stack.pop() if visited[i][j]: continue visited[i][j] = True """ Si una celda adyacente es una persona mala, queremos convertir la celda actual en una pared. Si una celda adyacente es una persona mala y la celda actual es una persona buena, no podemos proteger a la persona buena. Solo agregamos celdas adyacentes a la cola y marcamos a la persona buena como protegida si descubrimos que no hay personas malas adyacentes. """ cells_to_add = [] for new_i, new_j in [(i + 1, j), (i - 1, j), (i, j + 1), (i, j - 1)]: if not (0 <= new_i < n and 0 <= new_j < m) or visited[new_i][new_j]: continue if grid[new_i][new_j] == "." or grid[new_i][new_j] == "G": cells_to_add.append((new_i, new_j)) elif grid[new_i][new_j] == "B": break else: stack.extend(cells_to_add) if grid[i][j] == "G": good_people.discard((i, j)) print("Yes" if not good_people else "No")