본문 바로가기
CODING/BAEKJOON

[BAEKJOON] 7569번 토마토

by snow_white 2022. 3. 23.

이전에 토마토 문제를 다룬적이 있는데 아래 글 먼저 보고 오시면 좋을 것 같습니다.

오늘 문제는 이전 문제보다 조금 더 심화된 문제입니다.

2022.03.05 - [CODING/BAEKJOON] - [BEAKJOON] 7576번 토마토

 

[BEAKJOON] 7576번 토마토

1(익은 토마토)인 위치에서 하루가 지날 때마다 맞닿은 부분(0)이 1로 변경된다. 내 주변 탐색 후 변경하는 과정을 반복해야 한다. 너비우선탐색 문제 유형으로 큐에 삽입하여 과정을 하나씩 따라

snowwhite1106.tistory.com


 

0은 익혀야 할 토마토, 1은 익은 토마토, -1은 토마토가 없어서 방문하지 않아도 된다.

이번 문제는 토마토 상자가 n단으로 쌓아올려져 있고, 내 주변 상하좌우 방향 뿐만 아니라 현재 위치에서 쌓여있는 아래, 위 상자의 위치까지의 토마토를 고려해야 한다.

따라서 방향 배열 dx, dy를 상하좌우, 상자의 위 아래로 총 6가지로 생성한다.

N의 크기에 따라 위, 아래 상자로 나아가는 범위가 가변적이기 때문에 N을 입력 받은 후 아래와 같이 방향 배열을 초기화해준다.

순서대로 하, 상, 우, 좌, 아랫층, 윗층으로의 변화를 의미한다.

dx = new int[]{1, -1, 0, 0, -N, N};
dy = new int[]{0,  0, 1, -1,  0, 0};

 

 

만약 위의 그림과 같이 가로 M=5, 세로 N=3, 높이 H=3층의 토마토 상자가 있다고 할 때 위, 아래 상자 위치까지 고려해보자.

이차원배열 arr은 M * (N*H) 크기를 갖고, N의 배수씩 나누어 층을 구분할 수 있다.

 

현재 위치가 arr[4][2]라고 할 때 (x=4, y=2, arr[x][y]라 가정) 아래 상자는 arr[x-N], 위의 상자는 arr[x+N]위치가 된다.

 

상하좌우를 탐색할 때 고려해야할 범위가 있다. 기존 상하좌우 탐색은 현재 위치에서 위, 아래 상자로 넘어가면 안 되기 때문에 같은 층에서의 탐색만 허용한다. 즉 x의 범위를 조정해야 한다.

만약 현재 x위치가 4라면 2층을 의미하고, x의 범위는 아래와 같이 3~5로 제한해야 한다.

1층은 x>=0 && x<3   

2층은 x>=3 && x<6

3층은 x>=6 && x<9

위의 범위는 (현재 x 위치 / N ) * N로 구한다.

만약 내 x 위치가 4라면? (현재 x 위치 / N ) * N부터  (현재 x 위치 / N ) * N + N 까지

( 4 / 3 ) * 3으로 3이 나올 것이고, 범위는 최소 3부터 N을 더한 6까지일 것이다.

해당 범위를 벗어나지 않는 선에서 현재 위치에서의 기존 네 방향 탐색으로 상하좌우 범위에 맞추어 탐색하면 된다.

 

위, 아래 상자로 나아갈 때의 범위는 따로 지정하여 전체 배열 크기를 넘지 않는 범위로 제한한다.

 

import java.util.*;
import java.io.*;

public class Main {
	static int N, M, H;
	static int[] dx;
	static int[] dy;
	static String arr[][];
	static Queue<Point> queue = new LinkedList<>();
	static int cnt = 0, days = 0;
	public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		StringTokenizer st = new StringTokenizer(br.readLine());
		M = Integer.parseInt(st.nextToken());
		N = Integer.parseInt(st.nextToken());
		H = Integer.parseInt(st.nextToken());
		arr = new String[N*H][M];
		dx = new int[]{1,-1,0,0, -N,N};
		dy = new int[]{0,0,1,-1,0,0};
		for(int i=0; i<N*H; i++) {
			st = new StringTokenizer(br.readLine());
			for(int j=0; j<M; j++) {
				String new_one = st.nextToken();
				arr[i][j] = new_one;
				if(new_one.equals("1")) {
					queue.add(new Point(i,j));	// 익은 토마도 큐에 넣기
				}else if(new_one.equals("0")) // 익혀야 하는 토마토 개수
					cnt++;
			}			
		}
		bfs();
		System.out.println((cnt==0)? days:-1);
	}
	
	static void bfs() {
		while(cnt>0 && !queue.isEmpty()) {
			// 하루에 익힐 수 있는 토마토 모두 큐에 넣기!
			for(int s = queue.size(); s>0; s--) {
				Point current = queue.poll();
				
				int current_x = current.x;
				int current_y = current.y;
				// 내 위치에서 주변 상하좌우
				for(int i=0; i<4; i++) {
					int new_x = current_x+dx[i];
					int new_y = current_y+dy[i];
					int range = (current_x/N)*N;
					if(new_x>=range && new_x<range+N && new_y>=0 && new_y<M && arr[new_x][new_y].equals("0")) {
						queue.add(new Point(new_x, new_y));
						arr[new_x][new_y] = "1";
						cnt--;	// 익힌 토마토는 총 개수에서 빼주기
					}
				}
                // 내 위치에서 상자 단위로 위 아래
				for(int i=4; i<6; i++) {
					int new_x = current_x+dx[i];
					int new_y = current_y+dy[i];
					int range = (current_x/N)*N;
					if(new_x>=0 && new_x<N*H && new_y>=0 && new_y<M && arr[new_x][new_y].equals("0")) {
						queue.add(new Point(new_x, new_y));
						arr[new_x][new_y] = "1";
						cnt--;	// 익힌 토마토는 총 개수에서 빼주기
					}
				}
			}
			days++; // 하루에 익힐 수 있는 토마토 모두 익혔어!
		}
	}

	static class Point{
		int x,y;
		public Point(int x, int y) {
			this.x = x;
			this.y = y;
		}
	}
}

[출처]

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

 

7569번: 토마토

첫 줄에는 상자의 크기를 나타내는 두 정수 M,N과 쌓아올려지는 상자의 수를 나타내는 H가 주어진다. M은 상자의 가로 칸의 수, N은 상자의 세로 칸의 수를 나타낸다. 단, 2 ≤ M ≤ 100, 2 ≤ N ≤ 100,

www.acmicpc.net

'CODING > BAEKJOON' 카테고리의 다른 글

[BAEKJOON] 1914번 하노이탑  (0) 2022.03.24
[BAEKJOON] 7562번 나이트의 이동  (0) 2022.03.23
[BAEKJOON] 4963번 섬의 개수  (0) 2022.03.22
[BAEKJOON] 11724번 연결 요소의 개수  (0) 2022.03.22
[BAEKJOON] 1697번 숨바꼭질  (0) 2022.03.22

댓글