[백준] 7569 문제 - 토마토(Java) 문제 해결 방법과 풀이코드

이 토마토 문제는 골드5 문제로 실버 단계에서 풀던 bfs 공식 으로 풀리지가 않았다.

기존 bfs의 공식이라 함은 for문을 돌아서 1을 찾은 다음 바로 큐에 담고 방문체크를 한 다음에 bfs를 돌리는 것인데 똑같이 이렇게 푸니 절대로 풀리지 않는 문제가 있었다.

 

기존 풀이 코드

for (int i = 0; i < mapZ; i++) {
    for (int j = 0; j < mapY; j++) {
       for (int k = 0; k < mapX; k++) {
          if (map[i][j][k] == 1 && !isVisited[i][j][k]) {
             isVisited[i][j][k] = true;
             bfs(i, j, k);
          }
       }
    }
}

 

직면한 문제와 해결 방법

만약 입력이 아래 처럼 주어졌을 때 밑에 1을 찾아 바로 bfs 돌면 인접해 있는 것들 모두 방문하게 돼서  인접한 토마토가 있음에도 불구하고 토마토 익은 일수가 2가 돼 버린다.

 

2 2 1                      익은 일수
0 0          -->         1 2 
1 1                          0 0

 

어떻게 해결할지 고민 하다가 문제 해결 방법을 보니 for문에서 bfs() 돌리기전에 1을 모두 큐에 담는 것이 해결 방법 이였다.

 

개선 코드

for (int i = 0; i < mapZ; i++) {
    for (int j = 0; j < mapY; j++) {
       for (int k = 0; k < mapX; k++) {
          if (map[i][j][k] == 1 && !isVisited[i][j][k]) {
             isVisited[i][j][k] = true;
             queue.add(new int[] {i, j, k});
          }
       }
    }
}

bfs();

 

위 처럼 1을 찾아 모두 큐에 담고 마지막에 bfs() 돌려 해결 하였다.

2 2 1                      익은 일수
0 0          -->         1 1 
1 1                          0 0

 

전체 풀이 코드

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

//https://www.acmicpc.net/problem/7569
public class 토마토_7569 {
    /**
     * 아이디어
     * 1. 3차 for문을 돌아서 1인걸 찾아서 bfs() 실행시킨다.
     * 2. 토마토 상자의 값이 0 인데 방문체크가 false면 -1을 리턴한다.
     * 3. 토마토 익은 날짜를 담는 배열을 하나 만들어서 정답을 출력한다.
     * 4. 큐를 돌아서 방문할때마다 큐의 x,y,z의 값 + 1 한 값을 tomatoRipeDay 에 담는다.
     * 5. tomatoRipeDay에서 가장 큰 값을 출력한다.
     *
     * 자료구조
     * - 토마토 상자 : int[z][y][x] map
     * - 토마토가 익은 날짜 : int[z][y][x] tomatoRipeDay
     * - 방문 체크 : boolean[z][y][x] isVisited
     * - 가로이동 : int[] = new int[]{0, 1, 0, -1, 0, 0}
     * - 세로이동 : int[] = new int[]{1, 0, -1, 0, 0, 0}
     * - 위아래이동 : int[] = new int[]{0, 0, 0, 0, 1, -1}
     */

    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    static StringTokenizer st = null;

    static int[][][] map = null;
    static int[][][] tomatoRipeDay = null;
    static boolean[][][] isVisited = null;
    static int mapZ, mapY, mapX;
    static Queue<int[]> queue = new LinkedList<>();

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

    private static void input() throws Exception {
       //가로x, 세로y, 상자높이 z입력
       st = new StringTokenizer(br.readLine());
       mapX = Integer.parseInt(st.nextToken());
       mapY = Integer.parseInt(st.nextToken());
       mapZ = Integer.parseInt(st.nextToken());

       map = new int[mapZ][mapY][mapX];
       tomatoRipeDay = new int[mapZ][mapY][mapX];
       isVisited = new boolean[mapZ][mapY][mapX];

       //z, y, x 3중 for문 한줄 마다 map에 인입
       for (int i = 0; i < mapZ; i++) {
          for (int j = 0; j < mapY; j++) {
             st = new StringTokenizer(br.readLine());
             for (int k = 0; k < mapX; k++) {
                map[i][j][k] = Integer.parseInt(st.nextToken());
             }
          }
       }
    }

    public static void main(String[] args) throws Exception {
       input();

       solve();

       print();
    }

    private static void print() {
       //토마토 상자의 값이 0 인데 방문체크가 false면 -1을 리턴한다.
       for (int i = 0; i < mapZ; i++) {
          for (int j = 0; j < mapY; j++) {
             for (int k = 0; k < mapX; k++) {
                if (map[i][j][k] == 0 && !isVisited[i][j][k]) {
                   System.out.println(-1);
                   return;
                }
             }
          }
       }

       int answer = 0;
       //tomatoRipeDay에서 가장 큰 값을 출력한다.
       for (int i = 0; i < mapZ; i++) {
          for (int j = 0; j < mapY; j++) {
             for (int k = 0; k < mapX; k++) {
                answer = Math.max(tomatoRipeDay[i][j][k], answer);
             }
          }
       }
       System.out.println(answer);
    }

    private static void solve() {
       for (int i = 0; i < mapZ; i++) {
          for (int j = 0; j < mapY; j++) {
             for (int k = 0; k < mapX; k++) {
                if (map[i][j][k] == 1 && !isVisited[i][j][k]) {
                   isVisited[i][j][k] = true;
                   queue.add(new int[] {i, j, k});
                }
             }
          }
       }

       bfs();
    }

    private static void bfs() {
       while (!queue.isEmpty()) {
          int[] curZYX = queue.poll();
          int curZ = curZYX[0];
          int curY = curZYX[1];
          int curX = curZYX[2];
          int curDay = tomatoRipeDay[curZ][curY][curX];

          for (int i = 0; i < dx.length; i++) {
             int qz = curZ + dz[i];
             int qy = curY + dy[i];
             int qx = curX + dx[i];

             if (qz >= 0 && qz < mapZ && qy >= 0 && qy < mapY && qx >= 0 && qx < mapX
                && map[qz][qy][qx] == 0 && !isVisited[qz][qy][qx]) {

                isVisited[qz][qy][qx] = true;
                tomatoRipeDay[qz][qy][qx] = curDay + 1;
                queue.add(new int[] {qz, qy, qx});
             }
          }
       }
    }
}

댓글

Designed by JB FACTORY