Coding Test/백준

[백준] 7576 토마토 - 자바

lsh2613 2023. 9. 3. 23:18

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

 

7576번: 토마토

첫 줄에는 상자의 크기를 나타내는 두 정수 M,N이 주어진다. M은 상자의 가로 칸의 수, N은 상자의 세로 칸의 수를 나타낸다. 단, 2 ≤ M,N ≤ 1,000 이다. 둘째 줄부터는 하나의 상자에 저장된 토마토

www.acmicpc.net

문제 풀이

현재 좌표에서 상하좌우로 즉 인접한 좌표를 탐색하기 때문에 bfs를 통해 해결할 수 있다.

 

먼저 좌표 상 -1로 빈 공간이 있기 때문에 전부 다 익은 토마토인지 비교하기 위해 빈공간의 갯수를 체크해줘야 한다.

또한 Queue를 통해 익은 토마토를 하나씩 꺼내 인접한 토마토를 익게하며, queue에 모든 요소를 다 꺼내서 이 과정을 수행하는 게 한 사이클이다. 이해가 안 가더라도 이후 코드를 참고하자

// init()
for (int i = 0; i < N; i++) {
    st = new StringTokenizer(br.readLine());
    for (int j = 0; j < M; j++) {
        int now = Integer.parseInt(st.nextToken());
        board[i][j] = now;

        if (now == 1)
            ripeTomatoes.offer(new Tomato(i, j));
        if (now == -1)
            emptyCnt++;
    }
}

다음으로 상하좌우를 살피기 위한 좌표코드이다

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

위에서 언급했듯이 빈 공간이 생기기 때문에 익은 토마토의 갯수를 업데이트 해줘야 한다.

좌표가 4*6일 때 빈 공간이 3개라면 익은 토마토가 21개일 때 모든 토마토가 익은 토마토임을 확인할 수 있다

int depth = -1; // 맨 처음 익은 토마토를 꺼내는 행위는 기존에 있던 토마토를 꺼내기 때문에 -1부터 시작
int ripeTomatoesSize = ripeTomatoes.size(); // 익은 토마토 갯수 카운트

하루가 지나면 모든 익은 토마토가 상하좌우로 영향을 준다. 따라서 while문의 한 사이클은 '모든' 익은 토마토가 옆으로 영향을 줄 때 하루가 지난다. 따라서 while 반복문의 사이클은 for문을 통해 모든 익은 토마토를 꺼냈을 때이다. 이 과정이 반복되는 것이다.

static Queue<Point> ripeTomatoes = new LinkedList<>();
while (!ripeTomatoes.isEmpty()) {
    depth++;
    int size = ripeTomatoes.size(); // for문 안의 ripeTomatoes에 요소를 추가하는 ripeTomatoes.offer가 있기 때문에
                                    // for 조건문의 바로 size()메소드를 쓰면 연산 횟수가 꼬임

    for (int i = 0; i < size; i++) {
        Point now = ripeTomatoes.poll();
        for (int j = 0; j < 4; j++) {
            int nx = now.x + dx[j];
            int ny = now.y + dy[j];

            // 범위를 벗어나거나, 0이 아닌 경우는 제외
            if (outOfScope(nx, ny) || board[nx][ny] != 0)
                continue;

            board[nx][ny] = 1;
            ripeTomatoesSize++;
            ripeTomatoes.offer(new Tomato(nx, ny));
        }
    }
}
static boolean outOfScope(int nx, int ny) {
    if (nx < 0 || ny < 0 || nx >= N || ny >= M)
        return true;
    return false;
}

코드

package 백준;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.LinkedList;
import java.util.Queue;
import java.util.StringTokenizer;

class Tomato {
    int x;
    int y;

    public Tomato(int x, int y) {
        this.x = x;
        this.y = y;
    }
}

public class b7576 {
    static int N, M;
    static int[][] board;
    static Queue<Tomato> ripeTomatoes = new LinkedList<>();
    static int emptyCnt = 0;
    static int fullSize;
    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());
        board = new int[N][M];

        // init()
        for (int i = 0; i < N; i++) {
            st = new StringTokenizer(br.readLine());
            for (int j = 0; j < M; j++) {
                int now = Integer.parseInt(st.nextToken());
                board[i][j] = now;

                if (now == 1)
                    ripeTomatoes.offer(new Tomato(i, j));
                if (now == -1)
                    emptyCnt++;
            }
        }

        fullSize = N * M;
        System.out.println(ripeTomatoes.size() == fullSize ? "0" : bfs());
    }

    static int[] dx = {-1, 1, 0, 0};
    static int[] dy = {0, 0, -1, 1};
    static int bfs() {
        int depth = -1; // 맨 처음 익은 토마토를 꺼내는 행위는 기존에 있던 토마토를 꺼내기 때문에 -1부터 시작
        int ripeTomatoesSize = ripeTomatoes.size(); // 익은 토마토 갯수 카운트

        while (!ripeTomatoes.isEmpty()) {
            depth++;
            int size = ripeTomatoes.size(); // for문 안의 ripeTomatoes에 요소를 추가하는 ripeTomatoes.offer가 있기 때문에
                                            // for 조건문의 바로 size()메소드를 쓰면 연산 횟수가 꼬임

            for (int i = 0; i < size; i++) {
                Tomato now = ripeTomatoes.poll();
                for (int j = 0; j < 4; j++) {
                    int nx = now.x + dx[j];
                    int ny = now.y + dy[j];

                    // 범위를 벗어나거나, 0이 아닌 경우는 제외
                    if (outOfScope(nx, ny) || board[nx][ny] != 0)
                        continue;

                    board[nx][ny] = 1;
                    ripeTomatoesSize++;
                    ripeTomatoes.offer(new Tomato(nx, ny));
                }
            }
        }
        return ripeTomatoesSize == fullSize - emptyCnt ? depth : -1;
    }

    static boolean outOfScope(int nx, int ny) {
        if (nx < 0 || ny < 0 || nx >= N || ny >= M)
            return true;
        return false;
    }
}