BOJ 풀이

[BOJ/백준 11967/C++] 불켜기

Vfly 2023. 2. 14. 21:22

https://www.acmicpc.net/problem/11967

 

11967번: 불켜기

(1, 1)방에 있는 스위치로 (1, 2)방과 (1, 3)방의 불을 켤 수 있다. 그리고 (1, 3)으로 걸어가서 (2, 1)방의 불을 켤 수 있다. (2, 1)방에서는 다시 (2, 2)방의 불을 켤 수 있다. (2, 3)방은 어두워서 갈 수 없으

www.acmicpc.net

 

[문제 요약]

- 농부 존은 최근에 N × N개의 방이 있는 거대한 헛간을 새로 지었다. 각 방은 (1, 1)부터 (N,N)까지 번호가 매겨져있다(2 ≤ N ≤ 100). 어둠을 무서워하는 암소 베시는 최대한 많은 방에 불을 밝히고 싶어한다.

 

- 베시는 유일하게 불이 켜져있는 방인 (1, 1)방에서 출발한다. 어떤 방에는 다른 방의 불을 끄고 켤 수 있는 스위치가 달려있다. 예를 들어, (1, 1)방에 있는 스위치로 (1, 2)방의 불을 끄고 켤 수 있다.

- 베시는 불이 켜져있는 방으로만 들어갈 수 있고, 각 방에서는 상하좌우에 인접한 방으로 움직일 수 있다.

 

- 베시가 불을 켤 수 있는 방의 최대 개수를 구하시오.

 

[문제 조건]

- 첫 번째 줄에는 N(2 ≤ N ≤ 100)과, M(1 ≤ M ≤ 20,000)이 정수로 주어진다.

- 다음 M줄에는 네 개의 정수 x, y, a, b가 주어진다. (x, y)방에서 (a, b)방의 불을 켜고 끌 수 있다는 의미이다.

- 한 방에 여러개의 스위치가 있을 수 있고, 하나의 불을 조절하는 스위치 역시 여러개 있을 수 있다.

- 시간 제한 : 2초

- 메모리 제한 : 512MB

 

[문제 풀이]

필자는 다음과 같은 순서로 문제를 생각해보았다.

  • 한 방에 여러개의 스위치가 있을 수 있음으로, 2차원배열에 원소는 vector<pair<int,int>>의 형태로 각 방별 스위치 정보를 저장한다.
  • 불이 켜져있고, 연결되어 있고 이미 지나온 방에 한해서 이동이 자유롭다고 생각한다.
  • 원소가 pair<int,int> 인 1차원 vector Remain 과 Use를 이용해 현재 상태에서 갈 수 있는 방과 불이켜져있고 현재상태에서 인접하게 이동가능 한 방을 나누어 생각한다.
  • Use를 확인하면서 추가적으로 현재 상태에서 갈 수 있는 방을 remain에 추가
  • 3~4과정을 반복하면서 변화가 없으면 종료

위처럼 생각했는데 한번 그림으로 알아보자

 

먼저 문제에서 주어진 예제를 다음과 같이 데이터를 저장한다.

1행 1열에는 1행2열, 1행3열의 불을 킬 수 있는 스위치가 있다라고 표시한 것이다.

마찬가지로 1행 3열에는 1행2열, 2행1열의 불을 킬 수 있는 스위치가 있다는 것이다.

 

다음으로 문제에서 이동은 1행 1열 부터 시작한다고 했으니 1행 1열부터 시작해보면

맨 처음에 (1,1)을 방문하면 (1,2)와 (1,3)의 불이 켜지게 된다. 그 다음 (1,1)은 불을 켜는데 사용했고 방문도 했으니 use 배열에 넣어주고, use배열의 크기만큼 상하좌우 인접한 칸들을 remain에 넣어준다.

이때 remain이 하는 역할은 아직 불이 켜지지 않은 방에 대해서도 나중에 그 방에 불이 켜질 수도 있기 때문에 저장해놓는 배열이다.

 

remain에 있는 좌표들을 확인하면서 불이 켜진 방에 대해서는 그 방으로 이동할 수 있다면 이동하고, 아직 이동이 안된다면 계속 remain에 남겨두는 방식인 것이다.

 

(1,2)에는 스위치가 없다.

위의 remain에서 불이 켜져 있는 방은 (1,3)밖에 없으므로 use에 (1,3)만 넣어준다.

 

바로 위의 경우에서 remain에는 더 이상 불이 켜져 있는 방이 남아있지 않다. 이때 break걸어주고 여태까지 방에 불을 켤때 cnt 변수를 이용해 하나씩 증가시키면서 불을 켜왔던 방의 개수를 구할 수 있다.

 

[소스 코드]

#include <iostream>
#include <algorithm>
#include <queue>
#include <vector>
#include <map>

#define INF 987654321
#define pii pair<int,int>

using namespace std;

int n, m;
int dx[4] = { 1, 0 , -1 , 0 };
int dy[4] = { 0, 1, 0 , -1 };
vector<vector<vector<pii>>> dt;
vector<vector<bool>> ck;
vector<vector<bool>> light;

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(NULL);
	cout.tie(NULL);

	cin >> n >> m;
	dt.resize(n + 1); ck.resize(n + 1); light.resize(n + 1);
	for (int i = 0; i < n + 1; i++) ck[i].resize(n+1);
	for (int i = 0; i < n + 1; i++) dt[i].resize(n+1);
	for (int i = 0; i < n + 1; i++) light[i].resize(n + 1);

	for (int i = 0; i < m; i++)
	{
		int srow, scol, erow, ecol;
		cin >> srow >> scol >> erow >> ecol;
		dt[srow][scol].push_back({ erow,ecol });
	}

	int cnt = 1;
	vector<pii> q;
	q.push_back({ 1,1 });
	light[1][1] = true; ck[1][1] = true;
	while (1)
	{
		vector<pii> remain;
		vector<pii> use;
		bool flag = false;
		for (int i = 0; i < q.size(); i++)
		{
			int row = q[i].first; int col = q[i].second;
			if (!light[row][col]) { remain.push_back({row,col}); continue; }

			flag = true;
			for (int j = 0; j < dt[row][col].size(); j++)
			{
				int sw_row = dt[row][col][j].first;
				int sw_col = dt[row][col][j].second;

				if (!light[sw_row][sw_col])
				{
					light[sw_row][sw_col] = true;
					cnt++;
				}
			}
			use.push_back({ row,col });
		}
		if (!flag) break;
		for (int i = 0; i < use.size(); i++)
		{
			int row = use[i].first; int col = use[i].second;

			for (int j = 0; j < 4; j++)
			{
				int nx = col + dx[j];
				int ny = row + dy[j];

				if (nx < 1 || ny < 1 || ny > n || nx > n) continue;
				if (ck[ny][nx]) continue;

				ck[ny][nx] = true;

				remain.push_back({ ny,nx });
			}
		}
		q.clear();
		q = remain;
	}
	cout << cnt;
}

약간 난해하게 설명한 부분도 있는 것 같은데

https://tobrother.tistory.com/53

 

[프로그래머스/Level 3/C++/2022 카카오 블라인드 채용] 양과 늑대

https://school.programmers.co.kr/learn/courses/30/lessons/92343 - 출처 : 프로그래머스 코딩 테스트 연습 프로그래머스 코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로

tobrother.tistory.com

이 문제 풀이법을 약간 응용해서 풀었다고 보면 된다.