Skip to Content

Switching on the Lights

Análisis oficial (Java) 

Pista 1

Primero hay que intentar programar un BFS normal para este problema. ¿Qué problemas concretos se notan?

Pista 2

Digamos que encendemos un interruptor. ¿Qué otras habitaciones podría afectar esto? ¿Qué habría que hacer para asegurarnos de que estos cambios se tengan en cuenta?

Solución en video

Por Hannah Ying

Nota: La solución en video podría no ser la misma que las otras soluciones. Código en Java.

Solución en video

Video de YouTube (qhTnF7RK4do)

Solución

Solución

Explicación

Podemos recorrer las habitaciones que están iluminadas y conectadas a la habitación (1,1)(1,1) usando flood fill, ya que esencialmente recorremos una componente conexa. Luego podemos encender todos los interruptores de cada habitación que visitamos.

Sin embargo, aparece un problema cuando una habitación recién iluminada que pasa a formar parte de la componente conexa principal no puede visitarse porque el flood fill ya visitó las habitaciones iluminadas vecinas.

Para tener esto en cuenta, empezamos un flood fill desde las habitaciones recién iluminadas si están conectadas a la componente conexa que contiene (1,1)(1,1) (es decir, si nos son accesibles). Llevando la cuenta de la cantidad de habitaciones que iluminamos por el camino, obtenemos la respuesta.

Implementación

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

#include <bits/stdc++.h> using namespace std; int N; int litRooms = 1; const int MN = 100; bool visited[MN][MN]; bool illuminated[MN][MN]; vector<pair<int, int>> switches[MN][MN]; int dirX[] = {-1, 0, 1, 0}; int dirY[] = {0, 1, 0, -1}; void setIO(string name = "") { cin.tie(0)->sync_with_stdio(0); if (name.size()) { freopen((name + ".in").c_str(), "r", stdin); freopen((name + ".out").c_str(), "w", stdout); } } // Comprueba si una habitación está conectada a la componente principal bool checkConnected(int x, int y) { // Iteramos sobre los vecinos for (int i = 0; i < 4; i++) { int newX = x + dirX[i]; int newY = y + dirY[i]; // Ignoramos el vecino si está fuera de los límites if (newX < 0 || newY < 0 || newX > N - 1 || newY > N - 1) { continue; } // Si un vecino está visitado, devolvemos true if (visited[newX][newY]) { return true; } } // Si ningún vecino ha sido visitado, devolvemos false return false; } // Método de flood fill con habitación origen (x,y) void floodfill(int x, int y) { // Ignoramos esta habitación si está fuera de los límites, ya fue visitada o no está iluminada if (x < 0 || y < 0 || x > N - 1 || y > N - 1 || visited[x][y] || !illuminated[x][y]) { return; } /* * Ignoramos la habitación si no está conectada a la componente principal * (es decir, Bessie no puede acceder a ella) * No retornamos en la coordenada (1, 1) */ if (!checkConnected(x, y) && !(x == 0 && y == 0)) { return; } // Marcamos la habitación como visitada visited[x][y] = true; // Iteramos sobre los vecinos y hacemos flood fill desde ellos for (int i = 0; i < 4; i++) { floodfill(x + dirX[i], y + dirY[i]); } // Encendemos todas las luces desde la habitación actual for (int i = 0; i < switches[x][y].size(); i++) { int roomX = switches[x][y][i].first; int roomY = switches[x][y][i].second; /* * Si la habitación aún no estaba iluminada, la sumamos a nuestro conteo * de habitaciones iluminadas */ if (!illuminated[roomX][roomY]) { litRooms++; } // Marcamos como iluminada la habitación a la que lleva el interruptor illuminated[roomX][roomY] = true; // Flood fill desde la nueva habitación iluminada floodfill(roomX, roomY); } } int main() { setIO("lightson"); int m; cin >> N >> m; // Leemos la entrada y agregamos los interruptores a la habitación correspondiente for (int i = 0; i < m; i++) { int x, y, a, b; cin >> x >> y >> a >> b; switches[x - 1][y - 1].push_back({a - 1, b - 1}); } // Marcamos la habitación superior izquierda como iluminada (está iluminada al inicio) illuminated[0][0] = true; // Empezamos flood fill desde la habitación superior izquierda floodfill(0, 0); cout << litRooms << endl; }
import sys sys.setrecursionlimit(100000) # Subimos el límite de recursión porque el default da error filein = open("lightson.in", "r") N, m = map(int, filein.readline().split()) lit_rooms = 1 visited = [[False for i in range(N)] for j in range(N)] illuminated = [[False for i in range(N)] for j in range(N)] switches = [[[] for i in range(N)] for j in range(N)] # Leemos la entrada de interruptores for i in range(m): x, y, a, b = map(int, filein.readline().split()) switches[x - 1][y - 1].append((a - 1, b - 1)) # Comprueba si una habitación está conectada a la componente principal def check_connected(x, y): dir_x = [-1, 0, 1, 0] dir_y = [0, -1, 0, 1] # Iteramos sobre los vecinos for i in range(4): new_x = x + dir_x[i] new_y = y + dir_y[i] # Ignoramos el vecino si está fuera de los límites if new_x < 0 or new_y < 0 or new_x > N - 1 or new_y > N - 1: continue # Si un vecino está visitado, devolvemos true if visited[new_x][new_y]: return True # Si ningún vecino ha sido visitado, devolvemos false return False # Método de flood fill con habitación origen (x, y) def floodfill(x, y): global lit_rooms # Ignoramos la habitación si está fuera de los límites, ya visitada o no iluminada if ( x < 0 or y < 0 or x > N - 1 or y > N - 1 or visited[x][y] or not illuminated[x][y] ): return # Ignoramos esta habitación si no está conectada a la componente principal # (es decir, Bessie no puede acceder a ella) # No retornamos en la coordenada inicial (1, 1) if not check_connected(x, y) and not (x == 0 and y == 0): return # Marcamos la habitación como visitada visited[x][y] = True dir_x = [-1, 0, 1, 0] dir_y = [0, -1, 0, 1] # Iteramos sobre los vecinos y hacemos flood fill desde ellos for i in range(4): floodfill(x + dir_x[i], y + dir_y[i]) # Encendemos todas las luces desde la habitación actual for i in range(len(switches[x][y])): room_x = switches[x][y][i][0] room_y = switches[x][y][i][1] # Si la habitación aún no estaba iluminada, la sumamos a nuestro conteo # de habitaciones iluminadas if not illuminated[room_x][room_y]: lit_rooms += 1 # Marcamos como iluminada la habitación a la que lleva el interruptor illuminated[room_x][room_y] = True # Flood fill desde la nueva habitación iluminada floodfill(room_x, room_y) # Marcamos la habitación superior izquierda como iluminada (está iluminada al inicio) illuminated[0][0] = True # Empezamos flood fill desde la habitación superior izquierda floodfill(0, 0) print(lit_rooms, file=open("lightson.out", "w"))
import java.io.*; import java.util.*; public class LightsOn { // Clase Pair de la solución oficial de USACO static class Pair { public int x, y; public Pair(int x, int y) { this.x = x; this.y = y; } } // Declaraciones iniciales static int N; static boolean illuminated[][]; static boolean visited[][]; static List<Pair>[][] switches; static int litRooms = 1; static int[] dirX = {0, 1, 0, -1}; static int[] dirY = {-1, 0, 1, 0}; // Comprueba si una habitación está conectada a la componente principal public static boolean checkConnected(int x, int y) { // Iteramos sobre los vecinos for (int i = 0; i < 4; i++) { int newX = x + dirX[i]; int newY = y + dirY[i]; // Ignoramos el vecino si está fuera de los límites if (newX < 0 || newY < 0 || newX > N - 1 || newY > N - 1) { continue; } // Si un vecino está visitado, la habitación está conectada a la // componente principal; devolvemos true if (visited[newX][newY]) { return true; } } // Si ningún vecino ha sido visitado, devolvemos false return false; } // Método de flood fill con habitación origen (x, y) public static void floodfill(int x, int y) { // Ignoramos esta habitación si está fuera de los límites, ya visitada o no iluminada if (x < 0 || y < 0 || x > N - 1 || y > N - 1 || visited[x][y] || !illuminated[x][y]) { return; } /* * Ignoramos la habitación si no está conectada a la componente principal * (es decir, Bessie no puede acceder a ella) * No retornamos en la coordenada (1, 1) */ if (!checkConnected(x, y) && !(x == 0 && y == 0)) { return; } // Marcamos la habitación como visitada visited[x][y] = true; // Iteramos sobre los vecinos y hacemos flood fill desde ellos for (int i = 0; i < 4; i++) { floodfill(x + dirX[i], y + dirY[i]); } // Encendemos todas las luces desde la habitación actual for (int i = 0; i < switches[x][y].size(); i++) { int roomX = switches[x][y].get(i).x; int roomY = switches[x][y].get(i).y; /* * Si la habitación aún no estaba iluminada, la sumamos a nuestro conteo * de habitaciones iluminadas */ if (!illuminated[roomX][roomY]) { litRooms++; } // Marcamos como iluminada la habitación a la que lleva el interruptor illuminated[roomX][roomY] = true; // Flood fill desde la nueva habitación iluminada floodfill(roomX, roomY); } } public static void main(String[] args) throws java.io.IOException { BufferedReader in = new BufferedReader(new FileReader("lightson.in")); PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter("lightson.out"))); StringTokenizer st = new StringTokenizer(in.readLine()); N = Integer.parseInt(st.nextToken()); int m = Integer.parseInt(st.nextToken()); illuminated = new boolean[N][N]; visited = new boolean[N][N]; switches = new ArrayList[N][N]; for (int i = 0; i < N; i++) { for (int j = 0; j < N; j++) { switches[i][j] = new ArrayList<Pair>(); } } // Leemos la entrada y agregamos los interruptores a la habitación correspondiente for (int i = 0; i < m; i++) { StringTokenizer st2 = new StringTokenizer(in.readLine()); int x = Integer.parseInt(st2.nextToken()); int y = Integer.parseInt(st2.nextToken()); int a = Integer.parseInt(st2.nextToken()); int b = Integer.parseInt(st2.nextToken()); switches[x - 1][y - 1].add(new Pair(a - 1, b - 1)); } // Marcamos la habitación superior izquierda como iluminada (está iluminada al inicio) illuminated[0][0] = true; // Empezamos flood fill desde la habitación superior izquierda floodfill(0, 0); out.println(litRooms); out.close(); } }