자바칩
[백준 / Java] 14502: 연구소 (백트래킹, BFS) 본문
난이도: Gold 4
문제: https://www.acmicpc.net/problem/14502
세로: N, 가로: M인 N * M 행렬이 있다.
3개의 벽(1)을 임시로 둔다.
바이러스(2)가 벽(1)을 제외하고 상하좌우로 퍼진다.
바이러스가 퍼지지 않은 구역은 남은 0의 개수를 세어 보면 된다.
총 27개가 나온다.
바이러스가 최소한으로만 퍼지게, 즉 안전 영역의 개수가 최대한 많게 3개의 벽을 세워야 한다.
N, M의 최대 개수가 8인 것을 보면 시간 복잡도에 전혀 영향을 끼치지 않는다.
벽을 3번 세우려면 최소 6중 for문을 돌려야 할 것 같은데,
8 * 8 * 8 * 8 * 8 * 8 = 262,144인 것을 보면 정말 시간 복잡도를 신경 쓰지 않아도 된다.
3개의 벽을 세우기 위해서는 브루트포스로 모든 경우의 수를 찾아봐야 한다.
1 0 1
0 0 2
0 2 0
예를 들어, 초기 단계를 위와 같다고 치자.
3개의 벽을 세우는 경우의 수는 정말 모두 일일히 찾아봐야 한다.
1 1 1
1 1 2
0 2 0
1 1 1
1 0 2
1 2 0
1 1 1
1 0 2
0 2 1
1 1 1
0 1 2
1 2 0
...
이런 식으로 임시로 둔 1이 맨 오른쪽에 있던 것부터 2중 for문을 돌리고,
맨 오른쪽에 있던 1이 맨 끝 좌표로 가면
중간에 있던 1을 오른쪽으로 1칸 옮기고 다시 맨 오른쪽에 있던 1을 돌리고....
이것의 반복이다.
즉,
for (1~N)
for (1~M)
for (1~N)
for (1~M)
for (1~N)
for (1~M)
인 것이다.
그리고 3개의 벽을 임시로 두면, 원래 0이었던 곳이 1로 바뀌게 된다.
저 작업을 끝내주면 다시 0으로 바꿔야 한다.
즉,
matrix[i][j] = 1
dfs(depth + 1) // 이 작업을 3번 해주고 함수가 끝나면
matrix[i][j] = 0 // 다시 원상복구 시켜주어야 한다.
이것이 바로 백트래킹이다.
바이러스를 상하좌우로 퍼뜨리는 것은 bfs로 하면 된다.
나는 바이러스가 퍼진 곳을 3으로 표시할 것이다.
자, 그럼 이제 코드를 보자.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
|
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.LinkedList;
import java.util.Queue;
import java.util.StringTokenizer;
public class P14502_연구소 {
static int N, M; // 행렬의 세로 크기, 가로 크기
static int[] dx = {-1, 0, 1, 0}; // 상, 하 이동
static int[] dy = {0, -1, 0, 1}; // 좌, 우 이동
static int[][] matrix; // 2차원 행렬 배열
static boolean[][] visited; // 2차원 방문 체크 배열
static int maxCount; // 안전 영역의 최대 크기
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
N = Integer.parseInt(st.nextToken());
M = Integer.parseInt(st.nextToken());
matrix = new int[N][M];
// 2차원 행렬 초기화
for (int i = 0; i < N; i++) {
st = new StringTokenizer(br.readLine());
for (int j = 0; j < M; j++) {
matrix[i][j] = Integer.parseInt(st.nextToken());
}
}
// N과 M의 크기가 작다.
// 이럴 경우에는 시간 복잡도 상관 없이 모든 경우의 수를 다 확인해 봐도 된다.
dfs(0);
System.out.println(maxCount);
}
public static void dfs(int depth) {
if (depth == 3) {
searchVirusZone();
int count = getCountOfSafetyZone();
maxCount = Math.max(maxCount, count);
return;
}
for (int i = 0; i < N; i++) {
for (int j = 0; j < M; j++) {
if (matrix[i][j] == 0) {
matrix[i][j] = 1;
dfs(depth + 1);
matrix[i][j] = 0;
}
}
}
}
public static void searchVirusZone() {
visited = new boolean[N][M];
for (int i = 0; i < N; i++) {
for (int j = 0; j < M; j++) {
if (!visited[i][j] && matrix[i][j] == 2) {
bfs(i, j);
}
}
}
}
public static void bfs(int a, int b) {
Queue<int[]> queue = new LinkedList<>();
queue.add(new int[] {a, b});
visited[a][b] = true;
while (!queue.isEmpty()) {
int[] now = queue.poll();
for (int i = 0; i < 4; i++) {
int x = now[0] + dx[i];
int y = now[1] + dy[i];
if (x < 0 || y < 0 || x >= N || y >= M) {
continue;
}
if (visited[x][y] || matrix[x][y] != 0) {
continue;
}
queue.add(new int[] {x, y});
visited[x][y] = true;
matrix[x][y] = 3;
}
}
}
public static int getCountOfSafetyZone() {
int count = 0;
for (int i = 0; i < N; i++) {
for (int j = 0; j < M; j++) {
if (matrix[i][j] == 0) {
count++;
} else if (matrix[i][j] == 3) {
matrix[i][j] = 0;
}
}
}
return count;
}
}
|
cs |
'알고리즘 > 백준' 카테고리의 다른 글
[백준 / Java] 2636: 치즈 (BFS) (0) | 2024.05.16 |
---|---|
[백준 / Java] 3190: 뱀 (시뮬레이션, 자료구조) (2) | 2024.05.15 |
[백준 / Java] 14503: 로봇 청소기 (시뮬레이션) (1) | 2024.05.15 |
[백준 / Java] 15686: 치킨 배달 (백트래킹) (0) | 2024.05.13 |
[백준 / Java] 2468: 안전 영역 (BFS) (0) | 2024.05.07 |