Skip to Content

Icy Perimeter

Análisis oficial (C++) 

Explicación

Podemos usar BFS para explorar cada mancha, empezando desde un # no visitado. Durante el BFS, podemos calcular el área de la mancha contando el número de # visitados y determinar su perímetro analizando el entorno de cada #.

El perímetro se determina sumando el número de lados de todas las celdas que están adyacentes a un # o están en el borde.

Al recorrer la grilla, podemos iniciar un BFS para cada # no visitado para computar el área y el perímetro de todas las manchas. Dentro del BFS, para cada #, empezamos asumiendo que sus cuatro lados contribuyen al perímetro. Luego reducimos este conteo en uno por cada # vecino, ya que las aristas compartidas no suman al perímetro.

Implementación

Complejidad temporal: O(N2)\mathcal{O}(N^2)

#include <bits/stdc++.h> using namespace std; // Direcciones de movimiento (arriba, abajo, izquierda, derecha) const vector<pair<int, int>> DIRECTIONS = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}}; pair<int, int> bfs(int row, int col, const vector<vector<char>> &grid, vector<vector<bool>> &visited) { const int n = grid.size(); // atajo queue<pair<int, int>> q; q.push({row, col}); // Empezamos BFS desde la celda dada (row, col) int area = 0; int perimeter = 0; while (!q.empty()) { auto [row, col] = q.front(); // Celda actual (row, col) q.pop(); // Saltamos si la celda ya fue visitada if (visited[row][col]) { continue; } visited[row][col] = true; // Marcamos la celda (row, col) como visitada area++; // Incrementamos el área (cada '#' aporta 1 al área) // Cada celda empieza con 4 lados que contribuyen al perímetro int sides = 4; for (auto [dx, dy] : DIRECTIONS) { int next_row = row + dx; int next_col = col + dy; // Comprobamos si la celda vecina está dentro de la grilla if (next_row >= 0 && next_row < n && next_col >= 0 && next_col < n) { // Si el vecino (next_row, next_col) es parte de la misma mancha if (grid[next_row][next_col] == '#') { // Agregamos el vecino (next_row, next_col) a la cola q.push({next_row, next_col}); sides--; // Las aristas compartidas reducen la contribución al perímetro } } } perimeter += sides; // Sumamos los lados restantes al perímetro } return {area, perimeter}; } int main() { freopen("perimeter.in", "r", stdin); freopen("perimeter.out", "w", stdout); int n; cin >> n; vector<vector<char>> grid(n, vector<char>(n)); vector<vector<bool>> visited(n, vector<bool>(n, false)); for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { cin >> grid[i][j]; } } int max_area = 0; int min_perimeter = INT_MAX; for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { if (grid[i][j] != '#' || visited[i][j]) { continue; } const auto [area, perimeter] = bfs(i, j, grid, visited); // Actualizamos max_area y min_perimeter según los resultados del BFS if (area > max_area || (area == max_area && perimeter < min_perimeter)) { max_area = area; min_perimeter = perimeter; } } } cout << max_area << " " << min_perimeter << endl; }
import java.io.*; import java.util.*; public class IcyPerimeter { // Direcciones de movimiento (arriba, abajo, izquierda, derecha) private static final int[][] DIRECTIONS = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}}; private static int[] bfs(int row, int col, char[][] grid, boolean[][] visited) { // Atajo para el tamaño de la grilla final int n = grid.length; Deque<int[]> q = new ArrayDeque<>(); q.add(new int[] {row, col}); // Empezamos BFS desde la celda dada (row, col) int area = 0; int perimeter = 0; while (!q.isEmpty()) { int[] current = q.poll(); // Celda actual (row, col) row = current[0]; col = current[1]; // Saltamos si la celda ya fue visitada if (visited[row][col]) { continue; } visited[row][col] = true; // Marcamos la celda (row, col) como visitada area++; // Incrementamos el área (cada '#' aporta 1 al área) // Cada celda empieza con 4 lados que contribuyen al perímetro int sides = 4; for (int[] direction : DIRECTIONS) { int nextRow = row + direction[0]; int nextCol = col + direction[1]; // Comprobamos si la celda vecina está dentro de la grilla if (nextRow >= 0 && nextRow < n && nextCol >= 0 && nextCol < n) { // Si el vecino (nextRow, nextCol) es parte de la misma mancha if (grid[nextRow][nextCol] == '#') { // Agregamos el vecino (nextRow, nextCol) a la cola q.add(new int[] {nextRow, nextCol}); sides--; // Las aristas compartidas reducen la contribución al perímetro } } } perimeter += sides; // Sumamos los lados restantes al perímetro } return new int[] {area, perimeter}; } public static void main(String[] args) throws IOException { BufferedReader br = new BufferedReader(new FileReader("perimeter.in")); int n = Integer.parseInt(br.readLine()); char[][] grid = new char[n][n]; boolean[][] visited = new boolean[n][n]; for (int i = 0; i < n; i++) { String line = br.readLine(); grid[i] = line.toCharArray(); } int maxArea = 0; int minPerimeter = Integer.MAX_VALUE; for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { if (grid[i][j] != '#' || visited[i][j]) { continue; } int[] result = bfs(i, j, grid, visited); int area = result[0]; int perimeter = result[1]; // Actualizamos maxArea y minPerimeter según los resultados del BFS if (area > maxArea || (area == maxArea && perimeter < minPerimeter)) { maxArea = area; minPerimeter = perimeter; } } } PrintWriter pw = new PrintWriter("perimeter.out"); pw.println(maxArea + " " + minPerimeter); pw.close(); } }
from collections import deque from typing import List, Tuple # Direcciones de movimiento (arriba, abajo, izquierda, derecha) DIRECTIONS = [(-1, 0), (1, 0), (0, -1), (0, 1)] def bfs( row: int, col: int, grid: List[str], visited: List[List[bool]] ) -> Tuple[int, int]: # Atajo para el tamaño de la grilla n = len(grid) queue = deque() queue.append((row, col)) # Empezamos BFS desde la celda dada (row, col) area = 0 perimeter = 0 while queue: row, col = queue.popleft() # Celda actual (row, col) # Saltamos si la celda ya fue visitada if visited[row][col]: continue visited[row][col] = True # Marcamos la celda (row, col) como visitada area += 1 # Incrementamos el área (cada '#' aporta 1 al área) # Cada celda empieza con 4 lados que contribuyen al perímetro sides = 4 for dx, dy in DIRECTIONS: next_row = row + dx next_col = col + dy # Comprobamos si la celda vecina está dentro de la grilla if 0 <= next_row < n and 0 <= next_col < n: # Si el vecino (next_row, next_col) es parte de la misma mancha if grid[next_row][next_col] == "#": # Agregamos el vecino (next_row, next_col) a la cola queue.append((next_row, next_col)) sides -= 1 # Las aristas compartidas reducen la contribución al perímetro perimeter += sides # Sumamos los lados restantes al perímetro return area, perimeter with open("perimeter.in", "r") as fin: n = int(fin.readline().strip()) grid = [fin.readline().strip() for _ in range(n)] visited = [[False] * n for _ in range(n)] max_area = 0 min_perimeter = float("inf") for i in range(n): for j in range(n): if grid[i][j] != "#" or visited[i][j]: continue area, perimeter = bfs(i, j, grid, visited) # Actualizamos max_area y min_perimeter según los resultados del BFS if area > max_area or (area == max_area and perimeter < min_perimeter): max_area = area min_perimeter = perimeter print(f"{max_area} {min_perimeter}", file=open("perimeter.out", "w"))