|
| 1 | +--- |
| 2 | +title: "Tomato (トマト)" |
| 3 | +description: "BOJ 7576 トマト問題の解説及びStringTokenizerの性能分析" |
| 4 | +date: 2026-01-20 16:50:00+09:00 |
| 5 | +categories: |
| 6 | + - Algorithm |
| 7 | +tags: |
| 8 | + - Java |
| 9 | + - BFS |
| 10 | + - BOJ |
| 11 | + - Algorithm |
| 12 | +--- |
| 13 | + |
| 14 | +# トマト (Tomato) |
| 15 | + |
| 16 | +- **所要時間**: 30分 |
| 17 | +- **解法アプローチ**: |
| 18 | + |
| 19 | +``` |
| 20 | +1. 問題文から「熟したトマトの隣接するトマトも熟していく」という点に着目しました。 |
| 21 | +2. 入力を受け取る際、値が「1(熟したトマト)」の座標をQueueに追加し、BFS(幅優先探索)を用いて熟していくトマトの日数を計算すればよいと考えました。 |
| 22 | +``` |
| 23 | + |
| 24 | +- **コード**: |
| 25 | + |
| 26 | +```java |
| 27 | +import java.io.BufferedReader; |
| 28 | +import java.io.IOException; |
| 29 | +import java.io.InputStreamReader; |
| 30 | +import java.util.LinkedList; |
| 31 | +import java.util.Queue; |
| 32 | +import java.util.StringTokenizer; |
| 33 | + |
| 34 | +class Main { |
| 35 | + public static void main(String[] args) throws IOException { |
| 36 | + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); |
| 37 | + |
| 38 | + StringTokenizer st = new StringTokenizer(br.readLine()); |
| 39 | + int m = Integer.parseInt(st.nextToken()); |
| 40 | + int n = Integer.parseInt(st.nextToken()); |
| 41 | + |
| 42 | + int[][] arr = new int[n][m]; |
| 43 | + |
| 44 | + Queue<int[]> queue = new LinkedList<>(); |
| 45 | + |
| 46 | + int[] dx = {-1, 1, 0, 0}; |
| 47 | + int[] dy = {0, 0, -1, 1}; |
| 48 | + |
| 49 | + for (int i = 0; i < n; i++) { |
| 50 | + st = new StringTokenizer(br.readLine()); |
| 51 | + for (int j = 0; j < m; j++) { |
| 52 | + arr[i][j] = Integer.parseInt(st.nextToken()); |
| 53 | + if (arr[i][j] == 1) { |
| 54 | + queue.add(new int[]{i, j}); |
| 55 | + } |
| 56 | + } |
| 57 | + } |
| 58 | + |
| 59 | + while (!queue.isEmpty()) { |
| 60 | + int[] current = queue.poll(); |
| 61 | + int cx = current[0]; |
| 62 | + int cy = current[1]; |
| 63 | + |
| 64 | + for (int i = 0; i < 4; i++) { |
| 65 | + int nx = cx + dx[i]; |
| 66 | + int ny = cy + dy[i]; |
| 67 | + |
| 68 | + if (nx >= 0 && nx < n && ny >= 0 && ny < m && arr[nx][ny] == 0) { |
| 69 | + arr[nx][ny] = arr[cx][cy] + 1; |
| 70 | + queue.add(new int[]{nx, ny}); |
| 71 | + } |
| 72 | + } |
| 73 | + } |
| 74 | + |
| 75 | + int result = 0; |
| 76 | + |
| 77 | + for (int i = 0; i < n; i++) { |
| 78 | + for (int j = 0; j < m; j++) { |
| 79 | + if (arr[i][j] == 0) { |
| 80 | + System.out.println(-1); |
| 81 | + return; |
| 82 | + } |
| 83 | + if (result < arr[i][j]) { |
| 84 | + result = arr[i][j]; |
| 85 | + } |
| 86 | + } |
| 87 | + } |
| 88 | + |
| 89 | + System.out.println(result - 1); |
| 90 | + } |
| 91 | +} |
| 92 | +``` |
| 93 | + |
| 94 | +- **学んだこと**: |
| 95 | + - 文字列処理(String)において、`StringTokenizer` の方が演算速度が速いことを確認しました。 |
| 96 | +- **反省点・改善点**: |
| 97 | + - 初期のコードは整理されておらず、冗長な部分が多くありました。 |
| 98 | + - 一時変数(temp variable)を使用してトマトの日数を計算するなど、不要な演算が含まれていました。 |
| 99 | + - 条件として「トマトが1であれば熟している」とされていましたが、1以上の値に対する制約が特になかったため、日数の計算を直接 `arr` 配列上で行うように改善しました。 |
| 100 | + |
| 101 | +下記のパフォーマンステストコードを作成し、`StringTokenizer` の方が演算速度が速いことを確認しました。 |
| 102 | + |
| 103 | +```java |
| 104 | +import java.util.StringTokenizer; |
| 105 | + |
| 106 | +class SpeedTest { |
| 107 | + public static void main(String[] args) { |
| 108 | + int DATA_SIZE = 10_000_000; |
| 109 | + System.out.println("Creating Data (" + DATA_SIZE + ")... Wait a moment."); |
| 110 | + |
| 111 | + StringBuilder sb = new StringBuilder(); |
| 112 | + for (int i = 0; i < DATA_SIZE; i++) { |
| 113 | + sb.append(i).append(" "); |
| 114 | + } |
| 115 | + String inputData = sb.toString(); |
| 116 | + |
| 117 | + System.out.println("Complete! Length: " + inputData.length()); |
| 118 | + System.out.println("--------------------------------------------------"); |
| 119 | + |
| 120 | + long startSplit = System.currentTimeMillis(); |
| 121 | + |
| 122 | + String[] splitResult = inputData.split(" "); |
| 123 | + int sumSplit = 0; |
| 124 | + for(String s : splitResult) { |
| 125 | + sumSplit += s.length(); |
| 126 | + } |
| 127 | + |
| 128 | + long endSplit = System.currentTimeMillis(); |
| 129 | + System.out.println("[String.split] Duration: " + (endSplit - startSplit) + "ms"); |
| 130 | + |
| 131 | + long startToken = System.currentTimeMillis(); |
| 132 | + |
| 133 | + StringTokenizer st = new StringTokenizer(inputData); |
| 134 | + int sumToken = 0; |
| 135 | + while(st.hasMoreTokens()) { |
| 136 | + String s = st.nextToken(); |
| 137 | + sumToken += s.length(); |
| 138 | + } |
| 139 | + |
| 140 | + long endToken = System.currentTimeMillis(); |
| 141 | + System.out.println("[StringTokenizer] Duration: " + (endToken - startToken) + "ms"); |
| 142 | + |
| 143 | + System.out.println("--------------------------------------------------"); |
| 144 | + System.out.println("Result: StringTokenizer is faster!! It's a figure of how fast StringTokenizer is." + |
| 145 | + (double)(endSplit - startSplit) / (endToken - startToken)); |
| 146 | + } |
| 147 | +} |
| 148 | +``` |
| 149 | + |
| 150 | +--- |
| 151 | +**問題リンク**: [https://www.acmicpc.net/problem/7576](https://www.acmicpc.net/problem/7576) |
0 commit comments