알고리즘/C++
[백준, C++] 11967번 불켜기
엑츄얼리
2024. 10. 11. 16:05
문제
https://www.acmicpc.net/problem/11967
해설
일반적인 풀이 방식은 새로운 방에 불을 켤 때마다 BFS에서 방문 여부를 체크하는 vis배열을 모두 0으로 초기화한 후, 재탐색을 진행합니다.
이 풀이의 시간복잡도 O(N) = N^4일 것입니다.
제가 구현한 방식의 시간복잡도 O(N) = N^2입니다. 즉, 위에서 언급한 새로운 방에 불을 켤 때마다 BFS에서 방문 여부를 체크하는 vis 배열을 초기화하는 과정을 진행하지 않습니다.
조건
BFS를 기반으로 구현하였습니다.
먼저 Queue에 삽입되는 좌표는 베시가 접근할 수 있는 방의 좌표입니다.
해설에서 언급한 새로운 방에 불을 켤 때마다 BFS에서 방문 여부를 체크하는 vis배열을 모두 0으로 초기화하는 이유는 새로 불이 켜진 방이 이미 vis == 1 로 초기화된(베시가 지나간) 방과 인접할 수 있기 때문입니다.
이 과정을 생략하기 위해, 새롭게 불이 켜진 방이 vis==1로 초기화된(베시가 지나간)방과 인접한지 확인 후, 인접할 경우에 Queue에 삽입하는 방식을 선택했습니다.
이 때, 발생할 것으로 추정되는 예외와 발생하지 않는 이유는 다음과 같습니다.
- 배시가 갈 수 있는 방이지만, 아직 vis==1로 초기화되지 않은 경우에 Queue에 새로운 방이 삽입되지 않음
=> 배시가 갈 수 있는 방이지만, 아직 vis==1로 초기화되지 않았기 때문에 배시가 해당 방에 접근했을 때, 새로운 방에도 접근 가능
결론적으로 Queue에 좌표가 삽입되는 경우는 위의 경우와 일반적인 BFS 방식인 vis==0이고 불이 켜진 방의 경우로 2가지입니다.
정답 코드
#include <sstream>
#include <iostream>
#include <vector>
#include <queue>
#include <stdio.h>
using namespace std;
int dx[4] = {0, 0, 1, -1};
int dy[4] = {1, -1, 0, 0};
int N, M;
vector<vector<vector<pair<int, int> > > > lightSwitch;
vector<vector<int> > board;
vector<vector<int> > vis;
int answer;
void input() {
cin >> N >> M;
lightSwitch = vector(N, vector(N, vector<pair<int, int> >()));
board = vector(N, vector(N, 0));
vis = vector(N, vector(N, 0));
answer = 1;
int x, y, a, b;
for (int i = 0; i < M; i++) {
cin >> x >> y >> a >> b;
lightSwitch[x - 1][y - 1].push_back({a - 1, b - 1});
}
}
void solution() {
board[0][0] = 1;
vis[0][0] = 1;
queue<pair<int, int> > q;
q.push({0, 0});
while (!q.empty()) {
pair<int, int> cur = q.front();
q.pop();
for (int i = 0; i < lightSwitch[cur.first][cur.second].size(); i++) {
pair<int, int> nextLightSwitch = lightSwitch[cur.first][cur.second][i];
if (board[nextLightSwitch.first][nextLightSwitch.second] == 0) {
board[nextLightSwitch.first][nextLightSwitch.second] = 1;
answer++;
for (int dir = 0; dir < 4; dir++) {
int nx = nextLightSwitch.first + dx[dir];
int ny = nextLightSwitch.second + dy[dir];
if (nx < 0 || ny < 0 || nx >= N || ny >= N)
continue;
if (board[nx][ny] == 1 && vis[nx][ny] == 1) {
q.push({nextLightSwitch.first, nextLightSwitch.second});
vis[nextLightSwitch.first][nextLightSwitch.second] = 1;
}
}
}
}
for (int dir = 0; dir < 4; dir++) {
int nx = cur.first + dx[dir];
int ny = cur.second + dy[dir];
if (nx < 0 || ny < 0 || nx >= N || ny >= N)
continue;
if (board[nx][ny] == 1 && vis[nx][ny] == 0) {
q.push({nx, ny});
vis[nx][ny] = 1;
}
}
}
}
void output() {
cout << answer;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
input();
solution();
output();
}
이전에 풀었을 때와 비교해서 288ms -> 8ms 로 단축에 성공했습니다.