Switching on the Lights
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
Solución
Solución
Explicación
Podemos recorrer las habitaciones que están iluminadas y conectadas a la habitación 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 (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:
#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();
}
}