中文
[BOJ 17114, Java] 超级番茄

[BOJ 17114, Java] 超级番茄

BOJ 17114,“超番茄”题的 Java 解法

题目链接

[BOJ 17114] (<https://www.acmicpc.net/problem/17114&gt;)

分类

实现(implementation), 图论(graphs), 图遍历(graph_traversal), 广度优先搜索(bfs)

说明

我个人认为这是一个很好地展示了BFS可以变得多么“臃肿”的题目。

虽然我很早就知道这道题的存在,且解法本身也已广为人知,但这次我再次体会到:即使解法广为人知,将其以自己的方式实现却是另一回事。

快速输入输出是基础,而在进行广度优先搜索时,如果不剔除冗余部分,就可能导致超时。

虽然不确定在Python中能否通过sys.stdin.readline解决,但在Java中幸运的是,仅通过BufferedReader就能处理。

解完这道题后查阅资料发现,似乎另有更巧妙的解法,但这里采用的是对经典BFS进行粗暴扩展的方式来解决。

  1. 将坐标信息输入到11维数组中

  2. 未成熟的西红柿进行计数,成熟的西红柿记录坐标

  3. 遍历成熟的西红柿,将相邻的未成熟西红柿加入成熟西红柿列表的同时减少计数值,根据条件重复该过程

  4. 若计数值变为0,则表示全部成熟,输出耗时天数

  5. 若计数未归零,但在对现有未成熟的西红柿重复执行第(3)步操作时数量未发生变化,则说明无法继续催熟,因此输出-1。

在解决此题时,处理第2步和第3步时若采用其他方式——**即每次循环都重新计数,或者不移除已使用过的成熟番茄的坐标,可能会导致超时……**我在此处纠结了很久才解决。

解题代码

代码相当长……但我认为这是因为使用了11层for循环,并且需要表示相邻坐标,所以无可奈何。


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

public class Main {

    public static int tomatoLeft;

    /**
     * 토마토가 익는 메커니즘을 반영하여 1일 뒤의 배열로 변경하는 함수
     */
    public static void ripeTomato(int[][][][][][][][][][][] target, Queue<int[]> tomatoQueue, int[] sizeInfo) {

        int[][] movements = new int[][]{
                new int[]{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
                new int[]{-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
                new int[]{0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0},
                new int[]{0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0},
                new int[]{0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0},
                new int[]{0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0},
                new int[]{0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0},
                new int[]{0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0},
                new int[]{0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0},
                new int[]{0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0},
                new int[]{0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0},
                new int[]{0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0},
                new int[]{0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0},
                new int[]{0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0},
                new int[]{0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0},
                new int[]{0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0},
                new int[]{0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0},
                new int[]{0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0},
                new int[]{0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0},
                new int[]{0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0},
                new int[]{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
                new int[]{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1},
        };

        ArrayList<int[]> ripeList = new ArrayList<>();

        for (int[] nowIdx : tomatoQueue) {

            for (int[] movement : movements) {

                boolean isNotOverIndex = true;

                for (int i = 0; i < 11; i++) {
                    if (0 > nowIdx[i] + movement[i] || nowIdx[i] + movement[i] >= sizeInfo[i]) {
                        isNotOverIndex = false;
                        break;
                    }
                }

                int nextIdx00 = nowIdx[0] + movement[0];
                int nextIdx01 = nowIdx[1] + movement[1];
                int nextIdx02 = nowIdx[2] + movement[2];
                int nextIdx03 = nowIdx[3] + movement[3];
                int nextIdx04 = nowIdx[4] + movement[4];
                int nextIdx05 = nowIdx[5] + movement[5];
                int nextIdx06 = nowIdx[6] + movement[6];
                int nextIdx07 = nowIdx[7] + movement[7];
                int nextIdx08 = nowIdx[8] + movement[8];
                int nextIdx09 = nowIdx[9] + movement[9];
                int nextIdx10 = nowIdx[10] + movement[10];

                if (

                        isNotOverIndex

                                && target[nextIdx00][nextIdx01][nextIdx02]
                                [nextIdx03][nextIdx04][nextIdx05][nextIdx06]
                                [nextIdx07][nextIdx08][nextIdx09][nextIdx10]
                                == 0

                ) {

                    ripeList.add(

                            new int[]{
                                    nextIdx00, nextIdx01, nextIdx02,
                                    nextIdx03, nextIdx04, nextIdx05, nextIdx06,
                                    nextIdx07, nextIdx08, nextIdx09, nextIdx10
                            }

                    );

                }

            }

        }

        tomatoQueue.clear();

        for (int[] ripeCoord : ripeList) {

            if (

                    target[ripeCoord[0]][ripeCoord[1]][ripeCoord[2]]
                            [ripeCoord[3]][ripeCoord[4]][ripeCoord[5]][ripeCoord[6]]
                            [ripeCoord[7]][ripeCoord[8]][ripeCoord[9]][ripeCoord[10]] == 0

            ) {

                target[ripeCoord[0]][ripeCoord[1]][ripeCoord[2]]
                        [ripeCoord[3]][ripeCoord[4]][ripeCoord[5]][ripeCoord[6]]
                        [ripeCoord[7]][ripeCoord[8]][ripeCoord[9]][ripeCoord[10]] = 1;

                tomatoLeft -= 1;

                tomatoQueue.add(ripeCoord);

            }

        }

    }

    public static void main(String[] args) throws IOException {

        BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter output = new BufferedWriter(new OutputStreamWriter(System.out));

        // 배열 사이즈 입력
        int[] storageSize = new int[11];
        StringTokenizer sizeInput = new StringTokenizer(input.readLine(), "[ ]");

        for (int i = 0; i < 11; i++) {
            storageSize[i] = Integer.parseInt(sizeInput.nextToken());
        }

        // 창고 정보 입력
        int[][][][][][][][][][][] tomatoStorage = new int[storageSize[0]][storageSize[1]][storageSize[2]]
                [storageSize[3]][storageSize[4]][storageSize[5]][storageSize[6]]
                [storageSize[7]][storageSize[8]][storageSize[9]][storageSize[10]];

        Queue<int[]> ripeTomatoQueue = new LinkedList<>();

        for (int idx10 = 0; idx10 < storageSize[10]; idx10++) {
            for (int idx09 = 0; idx09 < storageSize[9]; idx09++) {
                for (int idx08 = 0; idx08 < storageSize[8]; idx08++) {
                    for (int idx07 = 0; idx07 < storageSize[7]; idx07++) {
                        for (int idx06 = 0; idx06 < storageSize[6]; idx06++) {
                            for (int idx05 = 0; idx05 < storageSize[5]; idx05++) {
                                for (int idx04 = 0; idx04 < storageSize[4]; idx04++) {
                                    for (int idx03 = 0; idx03 < storageSize[3]; idx03++) {
                                        for (int idx02 = 0; idx02 < storageSize[2]; idx02++) {
                                            for (int idx01 = 0; idx01 < storageSize[1]; idx01++) {

                                                StringTokenizer storageInfo = new StringTokenizer(input.readLine(), "[ ]");

                                                for (int idx00Counter = 0; idx00Counter < storageSize[0]; idx00Counter++) {

                                                    int nowInput = Integer.parseInt(storageInfo.nextToken());

                                                    if (nowInput == 1) {

                                                        ripeTomatoQueue.add(
                                                                new int[]{
                                                                        idx00Counter, idx01, idx02,
                                                                        idx03, idx04, idx05, idx06,
                                                                        idx07, idx08, idx09, idx10
                                                                }
                                                        );

                                                    } else if (nowInput == 0) {

                                                        tomatoLeft += 1;

                                                    }

                                                    tomatoStorage[idx00Counter][idx01][idx02]
                                                            [idx03][idx04][idx05][idx06]
                                                            [idx07][idx08][idx09][idx10]
                                                            = nowInput;

                                                }

                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }

        // 지난 날짜 카운팅 변수 및 모두 익지 못하는 상황을 체크하기 위한 Flag 선언
        int dayCounter = 0;
        boolean cantComplete = false;

        // 모두 익지 않았다면 반복
        while (tomatoLeft > 0) {

            // 함수를 호출하여 기존 배열 깊은 복사 후 1일 뒤의 배열 확인
            int lastTomato = tomatoLeft;
            ripeTomato(tomatoStorage, ripeTomatoQueue, storageSize);

            // 날짜 1 카운트
            dayCounter += 1;

            // 아직 다 익지 않았지만 기존이랑 차이가 없다면 전부 익을 수 없는 상황이므로 Flag 변경 후 break
            if (tomatoLeft != 0 && tomatoLeft == lastTomato) {
                cantComplete = true;
                break;
            }

        }

        // 문제 조건에 따라서 출력
        if (cantComplete) {
            output.write("-1");
        } else {
            output.write(Integer.toString(dayCounter));
        }

        output.flush();
        output.close();

    }

}

댓글 작성

게시글에 대한 의견을 남겨 주세요.

댓글 0