문제출처: https://www.acmicpc.net/problem/11724


풀이

그래프를 그려서 연결 요소의 개수를 세주면 됩니다. 무방향그래프이기 때문에 u->v, v->u 길을 모두 1처리해줘야합니다.


코드

#include<iostream>
using namespace std;
int map[1001][1001];
int check[1001];
int n, m, u, v, cnt;
void dfs(int x);
int main()
{
	cin.tie(0);
	cin.sync_with_stdio(false);
	cin >> n >> m;
	for (int i=0;i<m;i++)
	{
		cin >> u >> v;
		map[u][v] = 1;
		map[v][u] = 1;
	}
	for (int i = 1; i <= n; i++)
	{
		if(!check[i])
		{
			dfs(i);
			cnt++;
		}
	}
	cout << cnt << '\n';
}
void dfs(int x)
{
	check[x] = 1;
	for (int i = 1; i <= n; i++)
	{
		if (!check[i] && map[x][i])
			dfs(i);
	}
}


결과

문제출처: https://www.acmicpc.net/problem/1012


DFS를 이용한 풀이입니다. BFS를이용한 풀이는 링크를 참조해주세요. https://jow1025.tistory.com/87


풀이

BFS풀이법에서 언급했듯이 배추의 요소의 갯수만 알면되므로 dfs연산을 돌리는 횟수만 알면됩니다. 


코드

#include<iostream>
#include<cstring>
using namespace std;
int map[50][50];
int check[50][50];
int dx[4] = { 0,0,-1,1 };
int dy[4] = { -1,1,0,0 };
int m, n, k;
//가로, 세로,배추위치갯수
void dfs(int y, int x);
int main()
{
	int t;
	cin >> t;
	int x, y;
	while (t--)
	{
		memset(map, 0, sizeof(map));
		memset(check, 0, sizeof(check));
		cin >> m >> n >> k;
		int ans = 0;
		for (int i = 0; i < k; i++)
		{
			//좌표조심
			cin >> x >> y;
			map[y][x] = 1;
		}
		for (int i = 0; i < n; i++)
		{
			for (int j = 0; j < m; j++)
			{
				if (map[i][j] == 1 && check[i][j] == 0)
				{
					dfs(i, j);
					ans++;
				}
			}
		}
		cout << ans << endl;
	}
}
void dfs(int y, int x)
{
	check[y][x] = 1;
	for (int i = 0; i < 4; i++)
	{
		int nx = x + dx[i];
		int ny = y + dy[i];
		if (nx >= 0 & ny >= 0 && nx < m && ny < n)
		{
			if (map[ny][nx] == 1 && check[ny][nx] == 0)
			{
				check[ny][nx] = 1;
				dfs(ny, nx);
			}
		}
	}
}


결과



문제출처: https://www.acmicpc.net/problem/1012


BFS를 이용한 풀이입니다. DFS를이용한 풀이는 링크를 참조해주세요. https://jow1025.tistory.com/88

풀이

배추흰지렁이의 최소 수는 문제에도 힌트가 있는데, 배추가 있는 부분의 영역의 갯수입니다. 말로하면 헷갈릴 수 있으니 아래 그림을 참고해주세요.





즉, 배추흰지렁이의 최소 수는 bfs or dfs연산의 횟수(조건에 맞게 돌렸을 때), 즉 배추의 요소의 갯수라고 할 수 있습니다. 좌표가 헷갈릴 수 있으니 그것만 주의하시면 되고, 다른것은 어렵지않으므로 설명은 소스코드로 대체하겠습니다.


코드

#include<iostream>
#include<queue>
#include<cstring>
using namespace std;
int map[50][50];
int check[50][50];
int dx[4] = { 0,0,-1,1 };
int dy[4] = { -1,1,0,0 };
int m, n, k;
//가로, 세로,배추위치갯수
void bfs(int y,int x);
int main()
{
	int t;
	cin >> t;
	int x, y;
	while (t--)
	{
		memset(map, 0, sizeof(map));
		memset(check, 0, sizeof(check));
		cin >> m >> n >> k;
		int ans = 0;
		for (int i = 0; i < k; i++)
		{
			//좌표조심
			cin >> x >> y;
			map[y][x] = 1;
		}
		for (int i = 0; i < n; i++)
		{
			for (int j = 0; j < m; j++)
			{
				if (map[i][j] == 1 && check[i][j] == 0)
				{
					bfs(i, j);
					ans++;
				}
			}
		}
		cout << ans << endl;
	}
}
void bfs(int y, int x)
{
	check[y][x] = 1;
	queue<pair<int, int>>q;
	q.push(make_pair(y, x));
	while (!q.empty())
	{
		y = q.front().first;
		x = q.front().second;
		q.pop();
		for (int i = 0; i < 4; i++)
		{
			int ny = y + dy[i];
			int nx = x + dx[i];
			if (ny >= 0 && nx >= 0 && nx < m && ny < n) {
				if (map[ny][nx] == 1 && check[ny][nx] == 0)
				{
					q.push({ ny,nx });
					check[ny][nx] = 1;
				}
			}
		}
	}
	
}


결과




'문제풀이(BOJ) > DFS,BFS' 카테고리의 다른 글

[백준 11724] 연결 요소의 개수(DFS)  (0) 2020.01.15
[백준 1012] 유기농 배추 (DFS)  (0) 2020.01.15
[백준 2178] 미로 탐색 (BFS)  (0) 2020.01.15
[백준 7576] 토마토 (BFS)  (0) 2020.01.15
[백준 2573] 빙산  (0) 2019.12.14

문제출처: https://www.acmicpc.net/problem/2178


풀이

최단거리구하기. 즉 dfs가 아닌 bfs를 사용해야합니다.  저는 방문 배열을 선언하여 0을 방문하지않았을 때를 의미하고 그외에 모든 양수값은 방문처리가 되었을때, 그리고 동시에 그값이 최단거리(칸)을 의미하게 함으로써 check[n-1][m-1]이 답이 될 수 있게끔 로직을 구성했습니다. 

1. map을 입력받습니다.

2. 0,0좌표를 시작으로 bfs를 돌립니다. 주위에 길이있고 방문하지않은 좌표를 최단칸으로 갱신합니다.(check배열로 갱신)

3. 최종적으로 check[n-1][m-1]은 0,0좌표부터 현재의 좌표까지의(답) 최단칸수입니다.


코드

#include<iostream>
#include<queue>
using namespace std;
char map[100][100];
int check[100][100];
int dx[4] = { 0,0,1,-1 };
int dy[4] = { -1,1,0,0 };
int n, m;
void bfs(int y, int x);
int main()
{
	cin >> n >> m;
	for (int i = 0; i < n; i++)
		cin >> map[i];
	//시작위치가 정해져있으므로 바로 bfs!
	bfs(0, 0);
	//15행 선언후 아래 행 먼저선언하자(어짜피 머릿속으로 이렇게 계산하자고생각했으므로)
	//check배열에 0,0부터 현재좌표까지의 최단칸수담음
	cout << check[n - 1][m - 1] << endl;
}
void bfs(int y,int x)
{
	queue<pair<int, int>>q;
	q.push({ y,x });
	check[y][x] = 1;
	while (!q.empty())
	{
		x=q.front().second;
		y=q.front().first;
		q.pop();
		for (int i = 0; i < 4; i++)
		{
			int nx = x + dx[i];
			int ny = y + dy[i];
			if (nx >= 0 && ny >= 0 && nx < m && ny < n)
			{
				//길이있고, 방문하지않았으면 방문 및 최단거리 갱신
				if (map[ny][nx] == '1' && check[ny][nx] == 0)
				{
					q.push({ ny,nx });
					check[ny][nx] = check[y][x] + 1;
				}
			}
		}
	}
}


결과

'문제풀이(BOJ) > DFS,BFS' 카테고리의 다른 글

[백준 1012] 유기농 배추 (DFS)  (0) 2020.01.15
[백준 1012] 유기농 배추 (BFS)  (0) 2020.01.15
[백준 7576] 토마토 (BFS)  (0) 2020.01.15
[백준 2573] 빙산  (0) 2019.12.14
[백준 10026] 적록색약  (0) 2019.12.14

문제출처: https://www.acmicpc.net/problem/7576


풀이

길게 늘여놓으면 이해가 안될 것같아 순서대로 풀이를 적겠습니다.

1. 입력받을 map, 좌표, 큐를 선언합니다.

2. 입력받으면서 익은토마토의 좌표를 큐에 넣습니다.

3. bfs함수를 돌립니다.

4. bfs함수에서, 익은 토마토좌표 주위의 안익은 토마토좌표의값을 익은 토마토좌표에 저장된값으로부터 +1해줍니다.

(bfs에서 익은 토마토좌표를이용해 연산하므로 1번에서 언급한 큐를 전역으로 선언한 이유입니다.)

5. 안익은토마토의 좌표를 +1해주는 이유는 0이아니게해주고(익게해주고), 궁극적으로는 답인 최소날짜를 구하기위함입니다. 익은게 1로 표현되므로 날짜를 구하기 좋은 조건입니다. 이 때, ans(날짜)를 0으로 선언하면 예제입력5를 입력하면 -1이 됨으로 1로 선언해둡니다.(예제코드를 입력해보다가 오류가 나서 발견했습니다.)

6. 주위 토마토를 익게하는 연산을 할 때 마다 ans(날짜)를 더 큰값으로 초기화해줍니다.

7. bfs연산을 마치고 map을 확인합니다. 좌표값이 0인 좌표가 존재하면 익을 수 없으므로 -1출력 뒤 종료합니다. 그리고 5번에서 설명했듯이 map의 좌표는 최소 일수들로 갱신된상태입니다. 그런데 연산최초에 익은 토마토주위의 값은 2인데, 2일이 지난게 아니라 1일이 지난것이므로 출력할 때 -1해줍니다.



주의할 것은 ans를 0이 아닌 1로 선언해야한다는 것입니다. 

(예제입력을 발견하다가 수정했더니 정답이네요,, 이부분에서 명확하게 1로선언해야하는 이유나 이렇게 애매한 선언을 방지하기위한 장치나, 다른 방법의연산방법이 있다면 알려주세요!!)


코드

#include<iostream>
#include<queue>
using namespace std;
int map[1000][1000];
int dx[4] = { 0,0,1,-1 };
int dy[4] = { -1,1,0,0 };
int m, n;
queue<pair<int, int>>q;
//ans를 0으로 해두면 예제입력5 설명불가
int ans = 1;
void bfs();
int main()
{
	cin.tie(0);
	cin.sync_with_stdio(false);
	cin >> m >> n;
	//토마토있는 좌표만 큐에넣음
	for (int i = 0; i < n; i++)
	{
		for (int j = 0; j < m; j++)
		{
			cin >> map[i][j];
			if (map[i][j] == 1)
			{
				q.push({ i,j });
			}
		}
	}
	bfs();
	//bfs를 돌리고나면 map은 토마토가익는 날(+1)로초기화됨
	//토마토있을때1이므로 그주변이 익을 때 1일이아닌 2일로표현되므로
	//답을 출력할때 -1해줘야함.
	for (int i = 0; i < n; i++)
	{
		for (int j = 0; j < m; j++)
		{
			//0이있으면 익지못한곳이있으므로 -1출력
			if (map[i][j] == 0)
			{
				cout << -1 << '\n';
				return 0;
			}
		}
	}
	//30~32행에서 언급했듯이 -1해줘야함. 
	cout << ans - 1 << '\n';
}
void bfs()
{
	while (!q.empty())
	{
		int x = q.front().second;
		int y = q.front().first;
		q.pop();
		for (int i = 0; i < 4; i++)
		{
			int nx = x + dx[i];
			int ny = y + dy[i];
			if (nx >= 0 && ny >= 0 && nx < m && ny < n)
			{
				if (map[ny][nx] == 0)
				{
					//익은곳주위의 토마토가안익은곳을 +1해줌
					//+1해주는 이유는 최소날짜를 구하기위함
					map[ny][nx] = map[y][x] + 1;
					q.push({ ny,nx });
					//초기화시켜줌.
					if (ans < map[ny][nx])
					{
						ans = map[ny][nx];
					}
				}
			}
		}
	}
}




'문제풀이(BOJ) > DFS,BFS' 카테고리의 다른 글

[백준 1012] 유기농 배추 (BFS)  (0) 2020.01.15
[백준 2178] 미로 탐색 (BFS)  (0) 2020.01.15
[백준 2573] 빙산  (0) 2019.12.14
[백준 10026] 적록색약  (0) 2019.12.14
[백준 2468] 안전 영역  (0) 2019.12.14

문제출처: https://www.acmicpc.net/problem/2573


문제

지구 온난화로 인하여 북극의 빙산이 녹고 있다. 빙산을 그림 1과 같이 2차원 배열에 표시한다고 하자. 빙산의 각 부분별 높이 정보는 배열의 각 칸에 양의 정수로 저장된다. 빙산 이외의 바다에 해당되는 칸에는 0이 저장된다. 그림 1에서 빈칸은 모두 0으로 채워져 있다고 생각한다.

그림 1. 행의 개수가 5이고 열의 개수가 7인 2차원 배열에 저장된 빙산의 높이 정보

빙산의 높이는 바닷물에 많이 접해있는 부분에서 더 빨리 줄어들기 때문에, 배열에서 빙산의 각 부분에 해당되는 칸에 있는 높이는 일년마다 그 칸에 동서남북 네 방향으로 붙어있는 0이 저장된 칸의 개수만큼 줄어든다. 단, 각 칸에 저장된 높이는 0보다 더 줄어들지 않는다. 바닷물은 호수처럼 빙산에 둘러싸여 있을 수도 있다. 따라서 그림 1의 빙산은 일년후에 그림 2와 같이 변형된다.

그림 3은 그림 1의 빙산이 2년 후에 변한 모습을 보여준다. 2차원 배열에서 동서남북 방향으로 붙어있는 칸들은 서로 연결되어 있다고 말한다. 따라서 그림 2의 빙산은 한 덩어리이지만, 그림 3의 빙산은 세 덩어리로 분리되어 있다.

한 덩어리의 빙산이 주어질 때, 이 빙산이 두 덩어리 이상으로 분리되는 최초의 시간(년)을 구하는 프로그램을 작성하시오. 그림 1의 빙산에 대해서는 2가 답이다. 만일 전부 다 녹을 때까지 두 덩어리 이상으로 분리되지 않으면 프로그램은 0을 출력한다.


입력

첫 줄에는 이차원 배열의 행의 개수와 열의 개수를 나타내는 두 정수 N과 M이 한 개의 빈칸을 사이에 두고 주어진다. N과 M은 3 이상 300 이하이다. 그 다음 N개의 줄에는 각 줄마다 배열의 각 행을 나타내는 M개의 정수가 한 개의 빈 칸을 사이에 두고 주어진다. 각 칸에 들어가는 값은 0 이상 10 이하이다. 배열에서 빙산이 차지하는 칸의 개수, 즉, 1 이상의 정수가 들어가는 칸의 개수는 10,000 개 이하이다. 배열의 첫 번째 행과 열, 마지막 행과 열에는 항상 0으로 채워진다.


출력

첫 줄에 빙산이 분리되는 최초의 시간(년)을 출력한다. 만일 빙산이 다 녹을 때까지 분리되지 않으면 0을 출력한다.


풀이

구현이 까다로워서 3시간넘게고민한 문제입니다. 아직 구현력이 많이 부족한것같습니다.... 알고리즘은 다음과 같습니다.

1년이 지날 때마다 빙산의 상태를 나타내는 temp배열을 선언합니다. map배열에 변화를 주면 0이아닌 좌표가 상태가 갱신되면 인접한 모든 좌표들도 즉각적으로 변화하기때문에 각각의 정확한 좌표의변화를 나타낼 수 없습니다. 따라서 temp배열에 변화된좌표를복사해야합니다. 그리고 각 좌표의 상하좌우를 살피며 빙산의상태를 갱신하고, temp가 완성되면 또 1년뒤의 변화를 나타낼 수 있도록 다시 map에 상태를 넘겨줍니다. 그리고 전형적인 dfs를 해주면되는데, 연결요소의 갯수를 ice라는 변수로 세주고, dfs까지의 연산이 끝나면 1년뒤의 상태변화를위한 연산이 끝나므로 year++해주면서 ice가 2이상일때 year를 출력해주면됩니다. 최종적으로 "1년뒤 빙산의상태 저장->복사->dfs수행"이 1년치의 한번의 연산이됩니다. 주석으로 나름 상세히 작성해보았습니다.


코드

#include<iostream>
#include<cstring>
using namespace std;
int dx[4] = { 0,0,1,-1 };
int dy[4] = { 1,-1,0,0 };
int temp[300][300];
int visited[300][300];
int map[300][300];
int year;
int n, m;
int cnt;
void dfs(int y,int x);
int main()
{
	cin >> n >> m;
	for (int i = 0; i < n; i++)
		for (int j = 0; j < m; j++)
			cin >> map[i][j];
	while (1)
	{
		memset(visited, 0, sizeof(visited));
		memset(temp, 0, sizeof(temp));
		for (int i = 1; i < n - 1; i++)
		{
			//1년의 연산 끝나면 32행 다시 반복하기위해 
			//복사했던 배열을 다시 map에복사하고 temp초기화해야함
			for (int j = 1; j < m - 1; j++)
			{
				cnt = 0;
				//0이아닌 좌표일때 
				if (map[i][j]>0)
				{
					//상하좌우돌아다니며 0이아닌 현재좌표의 내년의 남아있는 높이를 temp에저장
					for (int k = 0; k < 4; k++)
					{
						int ny = dy[k] + i;
						int nx = dx[k] + j;
						if (nx >= 0 && nx < m && ny >= 0 && ny < n)
						{
							if (map[ny][nx] == 0)
								cnt++;
						}
					}
					//다돌아다님,그좌표에서 cnt뺸값을 temp에저장
					int num = map[i][j] - cnt;
					if (num <= 0)temp[i][j] = 0;
					else temp[i][j] = num;
					
				}
			
			}
		}
		//높이깎고 1년뒤의모습출력했을때잘 나오는지 확인testcase
		/*for (int i = 0; i < n; i++) {
			for (int j = 0; j < m; j++) {
				cout << temp[i][j] << " ";
			}
			cout << endl;
		}*/
		for (int i = 0; i < n; i++)
		{
			for (int j = 0; j < m; j++)
			{
				map[i][j] = temp[i][j];
			}
		}
		int ice = 0;
		//1년지났으니 dfs를돌려서 연결요소갯수가 2개이상이면 year출력
		for (int i = 0; i < n; i++)
		{
			for (int j = 0; j < m; j++)
			{
				if (!visited[i][j] && map[i][j] >0)
				{
					dfs(i, j);
					ice++;
				}
					
			}
		}
		//빙산높이 감소시키고 dfs완료했으니 1년지남.
		year++;
		if (ice > 1) {
			cout << year << endl;
			return 0;
		}
		else if (ice == 0)
		{
			cout << 0 << endl;
			return 0;
		}

		
	}
	
}
void dfs(int y, int x)
{
	visited[y][x] = 1;
	for (int i = 0; i < 4; i++)
	{
		int ny = dy[i] + y;
		int nx = dx[i] + x;
		if (ny >= 0 && ny < n && nx >= 0 && nx < m) {
			if (!visited[ny][nx] && map[ny][nx]>0)
			{
				dfs(ny, nx);
			}
		}
	}
}


결과










 

'문제풀이(BOJ) > DFS,BFS' 카테고리의 다른 글

[백준 2178] 미로 탐색 (BFS)  (0) 2020.01.15
[백준 7576] 토마토 (BFS)  (0) 2020.01.15
[백준 10026] 적록색약  (0) 2019.12.14
[백준 2468] 안전 영역  (0) 2019.12.14
[백준 2583] 영역 구하기  (0) 2019.12.14

문제출처:https://www.acmicpc.net/problem/10026


문제

적록색약은 빨간색과 초록색의 차이를 거의 느끼지 못한다. 따라서, 적록색약인 사람이 보는 그림은 아닌 사람이 보는 그림과는 좀 다를 수 있다.

크기가 N×N인 그리드의 각 칸에 R(빨강), G(초록), B(파랑) 중 하나를 색칠한 그림이 있다. 그림은 몇 개의 구역으로 나뉘어져 있는데, 구역은 같은 색으로 이루어져 있다. 또, 같은 색상이 상하좌우로 인접해 있는 경우에 두 글자는 같은 구역에 속한다. (색상의 차이를 거의 느끼지 못하는 경우도 같은 색상이라 한다)

예를 들어, 그림이 아래와 같은 경우에

RRRBB
GGBBB
BBBRR
BBRRR
RRRRR

적록색약이 아닌 사람이 봤을 때 구역의 수는 총 4개이다. (빨강 2, 파랑 1, 초록 1) 하지만, 적록색약인 사람은 구역을 3개 볼 수 있다. (빨강-초록 2, 파랑 1)

그림이 입력으로 주어졌을 때, 적록색약인 사람이 봤을 때와 아닌 사람이 봤을 때 구역의 수를 구하는 프로그램을 작성하시오.


입력

첫째 줄에 N이 주어진다. (1 ≤ N ≤ 100)

둘째 줄부터 N개 줄에는 그림이 주어진다.


출력

적록색약이 아닌 사람이 봤을 때의 구역의 개수와 적록색약인 사람이 봤을 때의 구역의 수를 공백으로 구분해 출력한다.


풀이

색약일 때와 아닐때 각각 dfs를돌렸을 때의 연결요소 수를 출력하면됩니다. 색약일 때는 R->G 또는 G->R로 바꿔주면됩니다. 색약이 아닐 때 연산을 마치고 visited배열을 초기화해주고, dfs를 돌렸을 때 현재의 구간이 상하좌우의 구간과 문자가 같으면 계속해서 dfs를 돌려주면서 같은문자일때 체크해주면됩니다.


코드

#include<iostream>
#include<cstring>
int dx[4] = { 0,0,1,-1 };
int dy[4] = { 1,-1,0,0 };
using namespace std;
int n;
char map[101][101];
bool visited[101][101];
void dfs(int y,int x);
int main()
{
	cin >> n;
	//정상인의 연결갯수,색약인의 연결갯수
	int normal = 0, unnormal = 0;
	for (int i = 1; i <= n; i++)
	{
		for (int j = 1; j <= n; j++)
		{
			cin >> map[i][j];
		}
	}
	//정상인,색약인 총 두번 dfs필요
	
	for (int i = 1; i <= n; i++)
	{
		for (int j = 1; j <= n; j++)
		{
			if (!visited[i][j])
			{
				dfs(i, j);
				normal++;
			}
		}
	}
	memset(visited, false, sizeof(visited));
	for (int i = 1; i <= n; i++)
	{
		for (int j = 1; j <= n; j++)
		{
			if (map[i][j] == 'R')map[i][j] = 'G';
		}
	}
	for (int i = 1; i <= n; i++)
	{
		for (int j = 1; j <= n; j++)
		{
			if (!visited[i][j])
			{
				dfs(i, j);
				unnormal++;
			}
		}
	}
	cout << normal << " " << unnormal << endl;
}
void dfs(int y, int x)
{
	visited[y][x] = true;
	for (int i = 0; i < 4; i++)
	{
		int ny = y + dy[i];
		int nx = x + dx[i];
		//맵과 닿는 모든구간이 맵의요소와 같아야 dfs돌림
		if (ny >= 1 && ny <= 100 && nx >= 1 && nx <= 100) {
			if (!(visited[ny][nx]) && (map[y][x] == map[ny][nx]))
			{
				dfs(ny, nx);
			}
		}
	}
}


결과




'문제풀이(BOJ) > DFS,BFS' 카테고리의 다른 글

[백준 7576] 토마토 (BFS)  (0) 2020.01.15
[백준 2573] 빙산  (0) 2019.12.14
[백준 2468] 안전 영역  (0) 2019.12.14
[백준 2583] 영역 구하기  (0) 2019.12.14
[백준 11724] 연결 요소의 개수  (0) 2019.12.14

문제출처: https://www.acmicpc.net/problem/2468


문제

재난방재청에서는 많은 비가 내리는 장마철에 대비해서 다음과 같은 일을 계획하고 있다. 먼저 어떤 지역의 높이 정보를 파악한다. 그 다음에 그 지역에 많은 비가 내렸을 때 물에 잠기지 않는 안전한 영역이 최대로 몇 개가 만들어 지는 지를 조사하려고 한다. 이때, 문제를 간단하게 하기 위하여, 장마철에 내리는 비의 양에 따라 일정한 높이 이하의 모든 지점은 물에 잠긴다고 가정한다.

어떤 지역의 높이 정보는 행과 열의 크기가 각각 N인 2차원 배열 형태로 주어지며 배열의 각 원소는 해당 지점의 높이를 표시하는 자연수이다. 예를 들어, 다음은 N=5인 지역의 높이 정보이다.

이제 위와 같은 지역에 많은 비가 내려서 높이가 4 이하인 모든 지점이 물에 잠겼다고 하자. 이 경우에 물에 잠기는 지점을 회색으로 표시하면 다음과 같다. 

물에 잠기지 않는 안전한 영역이라 함은 물에 잠기지 않는 지점들이 위, 아래, 오른쪽 혹은 왼쪽으로 인접해 있으며 그 크기가 최대인 영역을 말한다. 위의 경우에서 물에 잠기지 않는 안전한 영역은 5개가 된다(꼭짓점으로만 붙어 있는 두 지점은 인접하지 않는다고 취급한다). 

또한 위와 같은 지역에서 높이가 6이하인 지점을 모두 잠기게 만드는 많은 비가 내리면 물에 잠기지 않는 안전한 영역은 아래 그림에서와 같이 네 개가 됨을 확인할 수 있다. 

이와 같이 장마철에 내리는 비의 양에 따라서 물에 잠기지 않는 안전한 영역의 개수는 다르게 된다. 위의 예와 같은 지역에서 내리는 비의 양에 따른 모든 경우를 다 조사해 보면 물에 잠기지 않는 안전한 영역의 개수 중에서 최대인 경우는 5임을 알 수 있다. 

어떤 지역의 높이 정보가 주어졌을 때, 장마철에 물에 잠기지 않는 안전한 영역의 최대 개수를 계산하는 프로그램을 작성하시오. 


입력

첫째 줄에는 어떤 지역을 나타내는 2차원 배열의 행과 열의 개수를 나타내는 수 N이 입력된다. N은 2 이상 100 이하의 정수이다. 둘째 줄부터 N개의 각 줄에는 2차원 배열의 첫 번째 행부터 N번째 행까지 순서대로 한 행씩 높이 정보가 입력된다. 각 줄에는 각 행의 첫 번째 열부터 N번째 열까지 N개의 높이 정보를 나타내는 자연수가 빈 칸을 사이에 두고 입력된다. 높이는 1이상 100 이하의 정수이다.


출력

첫째 줄에 장마철에 물에 잠기지 않는 안전한 영역의 최대 개수를 출력한다.


풀이

이 문제의 핵심은 "아무것도 안잠긴 영역이 있을 수 있다", "비의양은 0~높이-1까지 라는것입니다. 다른분들의 풀이를 봤는데 비의 양이 1부터~100까지로놓고 푼 분들도 많은데 풀이에 따라서 아무것도 안잠긴 영역은 무조건 존재할 수 없을  때도 있습니다. 비의양은 0(내리지않을 때)~ 높이까지라고 보는게 맞습니다. 이 때 비의 양이 높이까지라면 역시 안잠기는영역은 존재할 수 없습니다. 최대높이가 100까지이고 비의양에 100까지라서 비의양이 0~100까지일 때의 모든경우를 살펴야하는데  안잠길수있는영역이 있을수 있나요? 하지만 본 문제는 아무것도 안잠긴 영역이 있을 수 있다고 전제를깔아놓아서 2 1 1 1 1 일때는 0이 답이겠지만 이런 입력은 애초에 없다는 가정이 성립하므로 높이의 범위가 0~100, 0~99 둘다 되는것 같습니다. 

풀이는 간단합니다.  비의양이 1~100까지의 모든경우를 살피기위해 temp배열로 비의 양이 달라질때마다(커질 때 마다) 복사하여 dfs를 수행합니다. 그 때 마다 최대 안전구역을 갱신하여 최종적으로 가장크게 갱신된 값이 답이됩니다.  복사+높이가 달라질 때 마다 visited배열,temp배열 초기화 하는 것만 구현하고나면 나머지는 전형적인 dfs입니다.


코드

#include<iostream>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
int arr[101][101];
int temp[101][101];//모든경우를 복사해야됨
int visited[101][101];
int dx[4] = { -1,1,0,0 };
int dy[4] = { 0,0,1,-1 };
void dfs(int y, int x);
int n;
int main()
{
	//시작지점부터 시작지점의높이를뺀상태에서dfs돌림.
	//돌리고 다시 원상복구시키고
	cin >> n;
	for (int i = 1; i <= n; i++)
	{
		for (int j = 1; j <= n; j++)
		{
			cin >> arr[i][j];
		}
	}
	int res = 0;
	//높이는 무조건1이상이고 비의 양은 0부터임!!!안내릴수도있음
	//따라서 무조건 testcase는 res가 1이상의값가지는 경우임
	
	for (int height = 0; height <= 100; height++)
	{
		memset(visited, 0, sizeof(visited));
		memset(temp, 0, sizeof(temp));
		int cnt = 0;//복사한 배열에서 안전영역갯수
		//각 높이일 때르 저장
		for (int i = 1; i <= n;i++)
		{
			for (int j = 1; j <= n; j++)
			{
				//안잠길 때 비의양뺴줌
				//안해도되는연산임
				if (arr[i][j] > height)
					temp[i][j] = arr[i][j] - height;
				//잠길때(음수,0)모두 0처리해야되므로
				//근데이미 0으로초기화시킨상태니 안해도됨
				/*else
					arr[i][j] = 0;*/
			}
		}
		//dfs
		for (int i = 1; i <= n; i++)
		{
			for (int j = 1; j <= n; j++)
			{
				if (!visited[i][j] && temp[i][j])
				{
					dfs(i, j);
					cnt++;
				}
			}
		}
		res = max(res, cnt);
	}
	cout << res << endl;
}
void dfs(int y, int x)
{
	visited[y][x] = 1;
	for (int i = 0; i < 4; i++)
	{
		int ny = y + dy[i];
		int nx = x + dx[i];
		if (1 <= ny && ny <= n && 1 <= nx && nx <= n)
		{
			if (!visited[ny][nx] && temp[ny][nx])
				dfs(ny, nx);
		}
	}
}


결과




'문제풀이(BOJ) > DFS,BFS' 카테고리의 다른 글

[백준 7576] 토마토 (BFS)  (0) 2020.01.15
[백준 2573] 빙산  (0) 2019.12.14
[백준 10026] 적록색약  (0) 2019.12.14
[백준 2583] 영역 구하기  (0) 2019.12.14
[백준 11724] 연결 요소의 개수  (0) 2019.12.14

+ Recent posts