본문 바로가기
알고리즘/백준

[백준 16724] 피리 부는 사나이 (C++)

by fortissimo 2024. 3. 28.

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

문제


피리 부는 사나이 성우는 오늘도 피리를 분다.

성우가 피리를 불 때면 영과일 회원들은 자기도 모르게 성우가 정해놓은 방향대로 움직이기 시작한다. 성우가 정해놓은 방향은 총 4가지로 U, D, L, R이고 각각 위, 아래, 왼쪽, 오른쪽으로 이동하게 한다.

이를 지켜보던 재훈이는 더 이상 움직이기 힘들어하는 영과일 회원들을 지키기 위해 특정 지점에 ‘SAFE ZONE’ 이라는 최첨단 방음 시설을 만들어 회원들이 성우의 피리 소리를 듣지 못하게 하려고 한다. 하지만 예산이 넉넉하지 않은 재훈이는 성우가 설정해 놓은 방향을 분석해서 최소 개수의 ‘SAFE ZONE’을 만들려 한다. 

성우가 설정한 방향 지도가 주어졌을 때 재훈이를 도와서 영과일 회원들이 지도 어느 구역에 있더라도 성우가 피리를 불 때 ‘SAFE ZONE’에 들어갈 수 있게 하는 ‘SAFE ZONE’의 최소 개수를 출력하는 프로그램을 작성하시오.

 

입력


첫 번째 줄에 지도의 행의 수를 나타내는 N(1 ≤ N ≤ 1,000)과 지도의 열의 수를 나타내는 M(1 ≤ M ≤ 1,000)이 주어진다.

두 번째 줄부터 N개의 줄에 지도의 정보를 나타내는 길이가 M인 문자열이 주어진다.

지도 밖으로 나가는 방향의 입력은 주어지지 않는다.

 

출력


첫 번째 줄에 ‘SAFE ZONE’의 최소 개수를 출력한다.

 

문제 풀이


dfs와 union-find를 이용한 문제.

safe zone의 개수는 독립된 구역의 개수와 같으므로 dfs를 이용해 깊이 우선 탐색을 몇 번 진행했는지 확인하면 된다.

 

아래는 union-find로 푼 코드.

union을 dfs 방식으로 진행하지 않고 이중 반복문을 사용했으므로 반복문 한 번으로는 묶여야 하는 집합이 모두 묶이지 않을 수도 있다. 따라서 union 시 parent의 값이 바뀌는지 확인하는 flag로 모든 묶여야 하는 집합이 묶였는지 체크해주었다.

더보기
#include <iostream>
#include <set>
using namespace std;
int* parents;

int findParent(int x)
{
	if (parents[x] == x)
	{
		return x;
	}
	else
	{
		return parents[x] = findParent(parents[x]);
	}
}

void merge(int x, int y)
{
	int parentX = findParent(x);
	int parentY = findParent(y);
	if (parentY < parentX)
	{
		parents[parentX] = parentY;
	}
	else
	{
		parents[parentY] = parentX;
	}
}

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

	int N, M;
	string str;
	cin >> N >> M;
	parents = new int[N * M];
	char** dir = new char* [N];
	set<int> s;
	set<int>::iterator it;
	bool isChanged = false;
	for (int i = 0; i < N; i++)
	{
		dir[i] = new char[M];
		cin >> str;
		for (int j = 0; j < M; j++)
		{
			dir[i][j] = str.at(j);
		}
	}
	for (int i = 0; i < N * M; i++)
	{
		parents[i] = i;
	}
	while (true)
	{
		isChanged = false;
		for (int i = 0; i < N; i++)
		{
			for (int j = 0; j < M; j++)
			{
				int x, y;
				if (dir[i][j] == 'D')
				{
					x = findParent(i * M + j);
					y = findParent((i + 1) * M + j);
				}
				else if (dir[i][j] == 'L')
				{
					x = findParent(i * M + j - 1);
					y = findParent(i * M + j);
				}
				else if (dir[i][j] == 'R')
				{
					x = findParent(i * M + j + 1);
					y = findParent(i * M + j);
				}
				else
				{
					x = findParent(i * M + j);
					y = findParent((i - 1) * M + j);
				}
				if (x != y)
				{
					merge(x, y);
					isChanged = true;
				}
			}
		}
		if (isChanged == false)
		{
			break;
		}
	}

	for (int i = 0; i < N*M; i++)
	{
		it = s.find(parents[i]);
		if (it == s.end())
		{
			s.insert(parents[i]);
		}
	}
	cout << s.size() << "\n";
	return 0;
}

'알고리즘 > 백준' 카테고리의 다른 글

[백준 16943] 숫자 재배치(C++)  (0) 2024.03.30
[백준 7579] 앱 (C++)  (0) 2024.03.29
[백준 10775] 공항 (C++)  (0) 2024.03.27
[백준 20126] 교수님의 기말고사 (C++)  (0) 2024.03.26
[백준 10986] 나머지 합 (C++)  (0) 2024.03.25