일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- union-find
- 알고리즘
- DFS
- 투 포인터
- 분리 집합
- 위상정렬
- Meet in the middle
- SCC
- kapt
- MySQL
- 그래프
- 구현
- Linux
- kruskal
- MST
- DP
- 백트래킹
- disjoint set
- BindingAdapter
- springdoc
- miller-rabin
- 페르마 소정리
- Java
- concurreny
- BFS
- 위상 정렬
- tarjan
- spring boot
- 이분 탐색
- 누적 합
- Today
- Total
기맹기 개발 블로그
[BOJ 16946] 벽 부수고 이동하기 4 (Java) 본문
BOJ 16946 벽 부수고 이동하기 4
난이도 : 골드 2
https://www.acmicpc.net/problem/16946
16946번: 벽 부수고 이동하기 4
N×M의 행렬로 표현되는 맵이 있다. 맵에서 0은 이동할 수 있는 곳을 나타내고, 1은 이동할 수 없는 벽이 있는 곳을 나타낸다. 한 칸에서 다른 칸으로 이동하려면, 두 칸이 인접해야 한다. 두 칸이
www.acmicpc.net
전략
N, M <= 1000 이므로 N*M은 최대 100만이다.
만약 1인 모든 칸에서 그래프 탐색을 시작한다면 시간초과가 발생할 것이다.
따라서 0인 칸에 대해서 먼저 탐색을 다 해놓고 1인 칸의 상하좌우에서 탐색 결과를 더해주는 방식으로 접근하였다.
1. 0인 칸에 대해서 bfs로 인접한 칸들의 개수를 구한다. -> blankCount[r][c]
2. 1인 칸에 대해서 상하좌우 blankCount를 더한다.
시행착오
4 5
11001
00111
01010
10101
위의 예시를 보자.
1의 과정이 끝난 다음에 blankCount는 다음과 같이 기록될 것이다.
00220
33000
30101
01010
이 때 map[2][1] 자리에서는 상하좌우의 blankCount를 더하게 되는데
blankCount[1][1] 과 blankCount[2][0]은 같이 이어져있으므로 한 번만 더해야 한다.
이를 구분하기 위해서 bfs를 진행하고 blankCount에 저장할 때 groupId를 지정해줬다.
// 0인 위치들끼리 bfs 하여 count 에 이어진 개수 저장
static void bfs(int r, int c) {
Queue<Pos> q = new LinkedList<>();
List<Pos> visiting = new ArrayList<>();
q.offer(new Pos(r, c));
while (!q.isEmpty()) {
Pos pos = q.poll();
if (visited[pos.y][pos.x]) continue;
visiting.add(pos);
visited[pos.y][pos.x] = true;
for (int i = 0; i < 4; i++) {
int nr = pos.y + dy[i];
int nc = pos.x + dx[i];
if (nr < 0 || nr >= n || nc < 0 || nc >= m) continue;
if (map[nr][nc]) continue;
q.offer(new Pos(nr, nc));
}
}
for (Pos pos : visiting) {
blankCount[pos.y][pos.x] = visiting.size();
blankGroupId[pos.y][pos.x] = groupCounter;
}
groupCounter++;
}
이제 blankGroupId에는 다음과 같이 저장된다.
00110
22000
20304
05060
각 이어진 빈칸들은 유일한 id를 갖게 되고 벽에서 상하좌우의 blankCount를 더해줄 때 중복된 id를 더하지 않도록 하면 된다.
// 1인 위치들에서 인접한 0인 위치들 계산하여 result 에 저장
static void countBlock(int r, int c) {
result[r][c] = 1;
List<Integer> visitedGroup = new ArrayList<>();
for (int i = 0; i < 4; i++) {
int nr = r + dy[i];
int nc = c + dx[i];
if (nr < 0 || nr >= n || nc < 0 || nc >= m)
continue;
if (visitedGroup.contains(blankGroupId[nr][nc]))
continue;
result[r][c] += blankCount[nr][nc];
visitedGroup.add(blankGroupId[nr][nc]);
}
result[r][c] %= 10;
}
코드
package BOJ_16946;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
public class Main {
static int n, m;
// 1 -> true / 0 - > false
static boolean[][] map;
static boolean[][] visited;
// 연속된 0의 개수 저장
static int[][] blankCount;
// 연속된 0을 그룹으로 묶어서 자신이 속한 id를 저장
static int[][] blankGroupId;
static int groupCounter = 1;
static int[][] result;
final static int[] dy = { -1, 1, 0, 0 };
final static int[] dx = { 0, 0, -1, 1 };
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String[] inputs = br.readLine().split(" ");
n = Integer.parseInt(inputs[0]);
m = Integer.parseInt(inputs[1]);
map = new boolean[n][m];
visited = new boolean[n][m];
blankCount = new int[n][m];
result = new int[n][m];
blankGroupId = new int[n][m];
for (int i = 0; i < n; i++) {
String line = br.readLine();
for (int j = 0; j < m; j++) {
map[i][j] = line.charAt(j) == '1';
}
}
solution();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
sb.append(result[i][j]);
}
sb.append("\n");
}
System.out.println(sb);
}
static void solution() {
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (!map[i][j] && !visited[i][j])
bfs(i, j);
}
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (map[i][j]) {
countBlock(i, j);
}
}
}
}
// 1인 위치들에서 인접한 0인 위치들 계산하여 result 에 저장
static void countBlock(int r, int c) {
result[r][c] = 1;
List<Integer> visitedGroup = new ArrayList<>();
for (int i = 0; i < 4; i++) {
int nr = r + dy[i];
int nc = c + dx[i];
if (nr < 0 || nr >= n || nc < 0 || nc >= m)
continue;
if (visitedGroup.contains(blankGroupId[nr][nc]))
continue;
result[r][c] += blankCount[nr][nc];
visitedGroup.add(blankGroupId[nr][nc]);
}
result[r][c] %= 10;
}
// 0인 위치들끼리 bfs 하여 count 에 이어진 개수 저장
static void bfs(int r, int c) {
Queue<Pos> q = new LinkedList<>();
List<Pos> visiting = new ArrayList<>();
q.offer(new Pos(r, c));
while (!q.isEmpty()) {
Pos pos = q.poll();
if (visited[pos.y][pos.x]) continue;
visiting.add(pos);
visited[pos.y][pos.x] = true;
for (int i = 0; i < 4; i++) {
int nr = pos.y + dy[i];
int nc = pos.x + dx[i];
if (nr < 0 || nr >= n || nc < 0 || nc >= m) continue;
if (map[nr][nc]) continue;
q.offer(new Pos(nr, nc));
}
}
for (Pos pos : visiting) {
blankCount[pos.y][pos.x] = visiting.size();
blankGroupId[pos.y][pos.x] = groupCounter;
}
groupCounter++;
}
static class Pos {
final int y;
final int x;
public Pos(int y, int x) {
this.y = y;
this.x = x;
}
}
}
깨달은 점
이어진 공간을 묶는 유형의 그래프 문제가 꽤 있는 것 같은데, 이러한 방식으로 그룹을 구분하는 준비가 되어 있다면 훨씬 편할 것 같다.
이번 풀이에서는 간단하게 id를 저장하도록 하였는데 union-find 등을 사용할 수도 있을 것이다.
'PS' 카테고리의 다른 글
[BOJ 17404] RGB거리 2 (Java) (0) | 2022.12.29 |
---|---|
[BOJ 17143] 낚시왕 (Java) (0) | 2022.12.29 |
[BOJ 26157] 즉흥 여행 (Hard) (Java) (0) | 2022.12.27 |
[BOJ 2150] Strongly Connected Component (Java) (0) | 2022.12.27 |
[BOJ 2473] 세 용액 (Java) (1) | 2022.12.27 |