BOJ 2573 빙산

2021. 8. 13. 15:42백준 문제풀이

📎Problem Link

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

 

2573번: 빙산

첫 줄에는 이차원 배열의 행의 개수와 열의 개수를 나타내는 두 정수 N과 M이 한 개의 빈칸을 사이에 두고 주어진다. N과 M은 3 이상 300 이하이다. 그 다음 N개의 줄에는 각 줄마다 배열의 각 행을

www.acmicpc.net

 

📋 문제 정리

  • 각 칸에는 빙산의 각 부분별 높이 정보가 저장됨. 0은 바다를 뜻함.
  • 1년 마다 줄어드는 빙산의 높이 : 상하좌우로 접해있는 바다 면 수
  • 아무리 줄어들어도 0보다 더 줄어들진 않는다.
  • 첫 줄에는 이차원 배열의 행과 열을 나타내는 N, M 입력 (3 ≤ N ≤ 300)
  • 그 다음부터 N개의 줄에는 빙산의 높이 입력
  • 빙산 덩어리가 최소 2개 이상으로 쪼개지는 최소 시간(년) 구하기

📌 예제 1

예제를 그림으로 나타내면 다음과 같다.

 

🧐 Idea

  • DFS 함수로 전체를 한번 훑을 때 마다 각 칸 주변의 바다를 계산해서 -계산하기
  • 전체 훑어보면서 빙산 덩어리 수와 년 수 계산하기
  • 덩어리 수가 2 이상이 되면 모든 계산을 바로 종료하고 현재 년 수 출력하기 

💻 Code

int main() {
	map iceberg;
	int iceberg_count, year_count = 0;

	cin >> iceberg.N >> iceberg.M;

	for (int i = 0; i < iceberg.N; i++) {
		for (int j = 0; j < iceberg.M; j++) {
			cin >> iceberg.board[i][j];
		}
	} // 빙산 입력

	while (1) {
		iceberg_count = 0; 
		for (int i = 0; i < iceberg.N; i++) {
			for (int j = 0; j < iceberg.M; j++) {
				if (iceberg.board[i][j] && !iceberg.visited[i][j])
				{
					iceberg.visited[i][j] = true;
					iceberg.Timepass(i, j);
					iceberg_count++;
				}
			}
		}
		if (iceberg_count >= 2) 
		{
			cout << year_count; 
			return 0; 
		}
		else if (iceberg_count == 0) { 
			cout << 0;
			return 0;
		}

		iceberg.reset();

		year_count++;
	}
}

빙산 입력 후 while 문을 살펴보자.

현재 칸이 방문한 적이 없는 육지라면 현재 칸을 시작점으로 DFS를 시작한다. 이런 방식으로 전체를 한 번 다 훑고 나왔을 때 빙산 덩어리가 2 이상이라면 더 이상 DFS를 돌릴 필요가 없기 때문에 현재 년 수를 출력하고 프로그램을 종료한다.

여기서 중요한 점이 있다면 바로 그 다음에 있는 else if문이다. 만약 n년이 흘러 더 이상 녹을 빙산이 없다면 빙산 덩어리(iceberg_count)는 0이 된다. 그 뜻은 더 이상 시간을 더 지체하면 DFS를 돌릴 필요가 없다는 뜻이므로 0을 출력하고 프로그램을 종료한다. 9번의 제출 중 7번의 시간 초과가 났는데 이 코드 작성으로 '맞았습니다!'를 볼 수 있었다. 껄껄

 

 

void Timepass(int sy, int sx) {
		q.push(make_pair(sy, sx));

		while (!q.empty()) {
			int cy = q.front().first;
			int cx = q.front().second;
			visited[sy][sx] = 1;
			q.pop();
			visited[cy][cx] = 1;


			for (int i = 0; i < 4; i++) {
				int ny = cy + my[i];
				int nx = cx + mx[i];

				if (ny < 0 || ny >= N || nx < 0 || ny >= M) continue;

				if (!board[ny][nx])
				{
					watercount[cy][cx]++;
				}
				else
				{
					if (!visited[ny][nx])
					{
						q.push(make_pair(ny, nx));
						visited[ny][nx] = 1;
					}
				}
			}
		}
	}
    
    
    void reset() {
		for (int i = 0; i < N; i++) {
			for (int j = 0; j < M; j++) {
				board[i][j] = max(0, board[i][j] - watercount[i][j]);
				visited[i][j] = false;
				watercount[i][j] = 0;
			}
		}
	}

다음으로 Timepass와 reset함수를 살펴보자.

현재 칸을 기준으로 상하좌우에 있는 바다면을 확인하고 watercount라는 배열에 바다인 칸 수를 저장한다. 이렇게 전체를 한 번 싹 다 돌고 나와 reset함수를 호출한 후, 현재 값에서 바다면 수만큼 빼기 연산을 해준다. 이 때 유의해야할 점은 연산 값이 0 미만으로 내려가지 않도록 max 함수를 이용해 연산 결과가 0 미만이라면 그 칸의 값은 0이 되도록 한다. 그리고 다음 해에 전체를 훑기 위해 visited와 watercount 배열을 초기화한다.

 


'백준 문제풀이' 카테고리의 다른 글

BOJ 2146 다리 만들기  (0) 2021.08.15
BOJ 1707 이분 그래프  (0) 2021.08.13
BOJ 1987 알파벳  (0) 2021.08.06
BOJ 1389 케빈 베이컨의 6단계 법칙  (0) 2021.08.06
BOJ 1018 체스판 다시 칠하기  (0) 2021.08.06