Problem 🔒
문제
https://www.acmicpc.net/problem/16946
N×M의 행렬로 표현되는 맵이 있다. 맵에서 0은 이동할 수 있는 곳을 나타내고, 1은 이동할 수 없는 벽이 있는 곳을 나타낸다. 한 칸에서 다른 칸으로 이동하려면, 두 칸이 인접해야 한다. 두 칸이 변을 공유할 때, 인접하다고 한다.
각각의 벽에 대해서 다음을 구해보려고 한다.
- 벽을 부수고 이동할 수 있는 곳으로 변경한다.
- 그 위치에서 이동할 수 있는 칸의 개수를 세어본다.
한 칸에서 이동할 수 있는 칸은 상하좌우로 인접한 칸이다.
입력
첫째 줄에 N(1 ≤ N ≤ 1,000), M(1 ≤ M ≤ 1,000)이 주어진다. 다음 N개의 줄에 M개의 숫자로 맵이 주어진다.
출력
맵의 형태로 정답을 출력한다. 원래 빈 칸인 곳은 0을 출력하고, 벽인 곳은 이동할 수 있는 칸의 개수를 10으로 나눈 나머지를 출력한다.
더보기
예제 입력 1
3 3
101
010
101
예제 출력 1
303
050
303
예제 입력 2
4 5
11001
00111
01010
10101
예제 출력 2
46003
00732
06040
50403
Approach ⭕
- 빈 칸(0)에서 BFS를 돌려 빈 칸의 개수를 센다.
- 해당 위치에서 이동할 수 있는 모든 빈 칸에 개수를 저장한다.
- 그룹을 나누기 위해 그룹마다 인덱스를 생성하여 저장한다.
- 벽(1)을 방문해 4방향 탐색하여 이동가능한 칸 수, 인덱스를 저장한다.
- 중복되는 인덱스를 제외하고, 이동가능한 칸 수를 합하여 10으로 나눈다.
Solution Process 📝
- BFS 실행
- BFS 1번으로 이동할 수 있는 빈 칸의 개수를 구하여 저장까지 진행한다.
- 큐를 2개 생성하여, 큐1으로 빈 칸의 개수를 구하고, 큐2로 저장한다.
- 큐1을 빌 때까지 반복하며 이동할 수 있는 빈 칸이면 큐 2개에 좌표를 각각 추가하고, 개수를 증가시킨다.
- 큐2를 빌 때까지 반복하며 구한 개수를 이동가능한 빈 칸에 저장한다.
- 벽(1) 방문하여, 벽을 부수고 이동할 수 있는 칸의 개수를 세기
- 빠르게 출력하기 위해 StringBuilder에 결과 값을 저장한다.
- 4방향 탐색하며, arr[4][2]의 0번 열에 그룹번호, 1번 열에 빈 칸 개수를 저장한다.
- HashSet을 활용하여, HashSet에 그룹번호가 없으면 추가하고, list에 {그룹번호, 빈 칸 개수} 배열을 저장한다. (중복된 그룹 제거 과정)
- list에 있는 요소를 순회하며 빈 칸 개수를 더하여 10으로 나눈 값을 StringBuilder에 저장한다.
알게된 점 😯
static으로 Queue, HashSet, ArrayList를 선언으로 올린 후 메모리가 확연하게 줄었다.
반복적으로 변수, 자료구조 등을 선언하는 것은 메모리 소비가 많았다.
Solution 💡
public class 벽뿌4 {
// cost : 이동할 수 있는 칸 개수 저장, id : 그룹 인덱스 저장
static int N, M, map[][], cost[][], id[][], visit[][];
static int[] dr={-1,1,0,0}, dc={0,0,-1,1};
static Queue<int[]> q1 = new LinkedList<>(), q2 = new LinkedList<>();
static HashSet<Integer> hs = new HashSet<>();
static ArrayList<int[]> list = new ArrayList<>();
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());
map = new int[N][M]; cost = new int[N][M]; id = new int[N][M]; visit = new int[N][M];
for(int i=0; i<N; i++){
String s = br.readLine();
for(int j=0; j<M; j++){
map[i][j] = s.charAt(j)-'0';
}
}
// 0이면 BFS 실행
int idx = 1;
for(int i=0; i<N; i++){
for(int j=0; j<M; j++){
if(map[i][j]==0 && visit[i][j]!=1){
BFS(i,j,idx++);
}
}
}
// 벽(1)인곳 방문해서 답 구하기
StringBuilder sb = new StringBuilder();
for(int i=0; i<N; i++){
for(int j=0; j<M; j++){
// 빈 칸(0)이면 그대로 sb에 추가
if(map[i][j]==0){sb.append("0");}
else{
int[][] arr = new int[4][2]; // 0:idx, 1:cost
// 4방향 탐색하며, 그룹번호와 빈 칸 개수 저장
for(int d=0; d<4; d++){
int nr = i + dr[d];
int nc = j + dc[d];
if(nr>=0 && nr<N && nc>=0 && nc<M){
arr[d][0] = id[nr][nc]; // 그룹번호
arr[d][1] = cost[nr][nc]; // 빈 칸 개수
}
}
hs.clear();
list.clear();
// 중복된 그룹 제거과정
for(int[] row : arr){
// hashset에 그룹번호가 없으면
if(!hs.contains(row[0])){
hs.add(row[0]); // hashset에 그룹번호만 추가하고
list.add(row); // list에 {그룹번호, 빈 칸개수} 배열 저장
}
}
int ans = 1;
for(int[] get : list){
ans+=get[1];
}
sb.append(ans%10);
}
}
sb.append("\\n");
}
System.out.println(sb);
}
public static void BFS(int r, int c, int i){
visit[r][c] = 1;
q1.add(new int[]{r,c});
q2.add(new int[]{r,c});
int cnt = 1; // 이동 가능한 빈 칸 개수
// q1을 반복하며 빈 칸의 개수를 구한다.
while(!q1.isEmpty()){
int[] cur = q1.poll();
for(int d=0; d<4; d++){
int nr = cur[0] + dr[d];
int nc = cur[1] + dc[d];
// 이동할 수 있는 빈 칸이면 큐2개에 좌표를 각각 추가하고, 개수를 증가시킨다.
if(nr>=0 && nr<N && nc>=0 && nc<M && visit[nr][nc]==0 && map[nr][nc]==0){
cnt++;
visit[nr][nc]=1;
q1.add(new int[]{nr,nc});
q2.add(new int[]{nr,nc});
}
}
}
// q2를 반복하며 구한 빈 칸의 개수(cnt)를 이동가능한 빈 칸에 저장한다.
while(!q2.isEmpty()){
int[] get = q2.poll();
cost[get[0]][get[1]] = cnt;
id[get[0]][get[1]] = i;
}
}
}
'코딩 테스트(Coding Test) > 백준' 카테고리의 다른 글
[백준] 1931번 : 회의실 배정 - 자바(Java) (0) | 2024.12.03 |
---|---|
[백준] 17835번 : 면접보는 승범이네 - 자바(Java) (0) | 2024.12.01 |
[백준] 1043번 : 거짓말 - 자바(Java) (0) | 2024.11.28 |
[백준] 1922번 : 네트워크 연결 - 자바(Java) (0) | 2024.11.27 |
[백준] 1717번 : 집합의 표현 - 자바(Java) (0) | 2024.11.27 |