Skip to content

Commit 279d233

Browse files
committed
WaterWorld
1 parent b527396 commit 279d233

1 file changed

Lines changed: 288 additions & 0 deletions

File tree

Lines changed: 288 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,288 @@
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

Comments
 (0)