1+ /*
2+ * Принцип работы алгоритма:
3+ * Поле рассматривается как неориентированный граф: каждая клетка земли '#' — вершина,
4+ * а рёбра есть между соседними по стороне клетками земли. Идём по всем клеткам поля.
5+ * Если встречаем ещё не обработанную землю, значит найден новый остров. Запускаем из неё
6+ * итеративный BFS, помечаем все клетки этого острова как воду '.', одновременно считаем
7+ * размер острова и обновляем максимум.
8+ *
9+ * Почему алгоритм корректен:
10+ * Остров — это компонента связности клеток земли по четырём направлениям. BFS из любой
11+ * клетки такой компоненты посещает ровно все клетки этой компоненты: к каждой достижимой
12+ * по сторонам клетке он перейдёт, а за пределы компоненты не выйдет, потому что переходит
13+ * только в клетки '#'. После посещения клетки помечаются как '.', поэтому один и тот же
14+ * остров не будет посчитан повторно. Значит, каждое новое начало BFS соответствует ровно
15+ * одному острову, а посчитанный внутри BFS размер равен количеству клеток этого острова.
16+ * Перебор всех клеток гарантирует, что будут найдены все острова.
17+ *
18+ * Временная сложность: O(n * m), каждая клетка проверяется и обрабатывается не более одного раза.
19+ * Пространственная сложность: O(n * m), храним поле и очередь для BFS.
20+ */
21+
22+ import java .io .EOFException ;
23+ import java .io .IOException ;
24+ import java .io .InputStream ;
25+ import java .io .OutputStream ;
26+
27+ public class WaterWorld {
28+
29+ static int [] solve (byte [] map , int n , int m ) {
30+ int total = n * m ;
31+ int [] queue = new int [total ];
32+
33+ int islandCount = 0 ;
34+ int maxIslandSize = 0 ;
35+
36+ for (int start = 0 ; start < total ; start ++) {
37+ if (map [start ] != '#' ) {
38+ continue ;
39+ }
40+
41+ islandCount ++;
42+ int currentSize = 0 ;
43+ int head = 0 ;
44+ int tail = 0 ;
45+
46+ queue [tail ++] = start ;
47+ map [start ] = '.' ;
48+
49+ while (head < tail ) {
50+ int v = queue [head ++];
51+ currentSize ++;
52+
53+ int col = v % m ;
54+
55+ int up = v - m ;
56+ if (v >= m && map [up ] == '#' ) {
57+ map [up ] = '.' ;
58+ queue [tail ++] = up ;
59+ }
60+
61+ int down = v + m ;
62+ if (down < total && map [down ] == '#' ) {
63+ map [down ] = '.' ;
64+ queue [tail ++] = down ;
65+ }
66+
67+ int left = v - 1 ;
68+ if (col > 0 && map [left ] == '#' ) {
69+ map [left ] = '.' ;
70+ queue [tail ++] = left ;
71+ }
72+
73+ int right = v + 1 ;
74+ if (col + 1 < m && map [right ] == '#' ) {
75+ map [right ] = '.' ;
76+ queue [tail ++] = right ;
77+ }
78+ }
79+
80+ if (currentSize > maxIslandSize ) {
81+ maxIslandSize = currentSize ;
82+ }
83+ }
84+
85+ return new int []{islandCount , maxIslandSize };
86+ }
87+
88+ // -------------------- FAST INPUT --------------------
89+ static final class FastIn {
90+ private final InputStream in ;
91+ private final byte [] buf = new byte [1 << 16 ];
92+ private int ptr = 0 ;
93+ private int len = 0 ;
94+
95+ FastIn (InputStream in ) {
96+ this .in = in ;
97+ }
98+
99+ private int read () throws IOException {
100+ if (ptr >= len ) {
101+ len = in .read (buf );
102+ ptr = 0 ;
103+ if (len <= 0 ) {
104+ return -1 ;
105+ }
106+ }
107+ return buf [ptr ++];
108+ }
109+
110+ int nextInt () throws IOException {
111+ int c ;
112+ do {
113+ c = read ();
114+ if (c == -1 ) {
115+ throw new EOFException ("Unexpected EOF" );
116+ }
117+ } while (c <= ' ' );
118+
119+ int sign = 1 ;
120+ if (c == '-' ) {
121+ sign = -1 ;
122+ c = read ();
123+ }
124+
125+ int val = 0 ;
126+ while (c > ' ' ) {
127+ val = val * 10 + (c - '0' );
128+ c = read ();
129+ }
130+ return val * sign ;
131+ }
132+
133+ byte [] nextBytes (int length ) throws IOException {
134+ int c ;
135+ do {
136+ c = read ();
137+ if (c == -1 ) {
138+ throw new EOFException ("Unexpected EOF" );
139+ }
140+ } while (c <= ' ' );
141+
142+ byte [] result = new byte [length ];
143+ for (int i = 0 ; i < length ; i ++) {
144+ if (c <= ' ' ) {
145+ throw new EOFException ("Unexpected end of token" );
146+ }
147+ result [i ] = (byte ) c ;
148+ if (i + 1 < length ) {
149+ c = read ();
150+ }
151+ }
152+ return result ;
153+ }
154+ }
155+
156+ // -------------------- FAST OUTPUT --------------------
157+ static final class FastOut {
158+ private final OutputStream out ;
159+ private final byte [] buf = new byte [1 << 16 ];
160+ private int p = 0 ;
161+ private final byte [] tmp = new byte [12 ];
162+
163+ FastOut (OutputStream out ) {
164+ this .out = out ;
165+ }
166+
167+ void writeByte (int b ) throws IOException {
168+ if (p == buf .length ) {
169+ flush ();
170+ }
171+ buf [p ++] = (byte ) b ;
172+ }
173+
174+ void writeInt (int x ) throws IOException {
175+ if (x == 0 ) {
176+ writeByte ('0' );
177+ return ;
178+ }
179+ if (x < 0 ) {
180+ writeByte ('-' );
181+ x = -x ;
182+ }
183+
184+ int k = 0 ;
185+ while (x > 0 ) {
186+ tmp [k ++] = (byte ) ('0' + (x % 10 ));
187+ x /= 10 ;
188+ }
189+ for (int i = k - 1 ; i >= 0 ; i --) {
190+ writeByte (tmp [i ]);
191+ }
192+ }
193+
194+ void flush () throws IOException {
195+ out .write (buf , 0 , p );
196+ p = 0 ;
197+ }
198+ }
199+
200+ private static void run () throws Exception {
201+ FastIn in = new FastIn (System .in );
202+ FastOut out = new FastOut (System .out );
203+
204+ int n = in .nextInt ();
205+ int m = in .nextInt ();
206+ byte [] map = new byte [n * m ];
207+
208+ for (int row = 0 ; row < n ; row ++) {
209+ byte [] line = in .nextBytes (m );
210+ System .arraycopy (line , 0 , map , row * m , m );
211+ }
212+
213+ int [] answer = solve (map , n , m );
214+ out .writeInt (answer [0 ]);
215+ out .writeByte (' ' );
216+ out .writeInt (answer [1 ]);
217+ out .writeByte ('\n' );
218+ out .flush ();
219+ }
220+
221+ private static void test () {
222+ assertAnswer (5 , 1 , solve (map (
223+ "#.#" ,
224+ ".#." ,
225+ "#.#"
226+ ), 3 , 3 ));
227+
228+ assertAnswer (2 , 6 , solve (map (
229+ "#####" ,
230+ ".#..." ,
231+ "..#.." ,
232+ "#####"
233+ ), 4 , 5 ));
234+
235+ assertAnswer (0 , 0 , solve (map (
236+ "..." ,
237+ "..."
238+ ), 2 , 3 ));
239+
240+ assertAnswer (1 , 6 , solve (map (
241+ "###" ,
242+ "###"
243+ ), 2 , 3 ));
244+
245+ assertAnswer (4 , 1 , solve (map (
246+ "#.#" ,
247+ "..." ,
248+ "#.#"
249+ ), 3 , 3 ));
250+
251+ assertAnswer (2 , 3 , solve (map (
252+ "##." ,
253+ "#.." ,
254+ "..#"
255+ ), 3 , 3 ));
256+
257+ System .out .println ("Test OK" );
258+ }
259+
260+ private static byte [] map (String ... rows ) {
261+ int n = rows .length ;
262+ int m = rows [0 ].length ();
263+ byte [] result = new byte [n * m ];
264+ for (int row = 0 ; row < n ; row ++) {
265+ for (int col = 0 ; col < m ; col ++) {
266+ result [row * m + col ] = (byte ) rows [row ].charAt (col );
267+ }
268+ }
269+ return result ;
270+ }
271+
272+ private static void assertAnswer (int expectedCount , int expectedMaxSize , int [] actual ) {
273+ if (actual [0 ] != expectedCount || actual [1 ] != expectedMaxSize ) {
274+ throw new AssertionError (
275+ "Expected=" + expectedCount + " " + expectedMaxSize
276+ + ", actual=" + actual [0 ] + " " + actual [1 ]
277+ );
278+ }
279+ }
280+
281+ public static void main (String [] args ) throws Exception {
282+ if (System .getProperty ("os.name" ).startsWith ("Windows" )) {
283+ test ();
284+ } else {
285+ run ();
286+ }
287+ }
288+ }
0 commit comments