https://www.acmicpc.net/problem/2573
사용 알고리즘
- BFS
- 구현
풀이
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;
public class Main {
/*
1. 4분면에 있는 0의 개수만큼 값이 줄어듬
2. 두 덩어리로 분리되는 최초의 시간을 구함
*/
static int n, m;
static int [][] board;
static int [][] tmp;
static boolean[][] visited;
static int[] dx = {1, 0, -1, 0};
static int[] dy = {0, 1, 0, -1};
static int year = 0;
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());
board = new int[n][m];
for(int i = 0; i < n; i++){
st = new StringTokenizer(br.readLine());
for(int j = 0; j < m; j++){
board[i][j] = Integer.parseInt(st.nextToken());
}
}
int min = 0;
while (year <= 1) {
bfs();
min++;
if (allMelt()) {
System.out.println(0);
return;
}
}
System.out.println(min - 1);
}
public static void bfs() {
Queue<int[]> queue = new LinkedList<>();
visited = new boolean[n][m];
tmp = new int[n][m];
year = 0;
for(int i = 0; i < n; i++){
for(int j = 0; j < m; j++){
if (!visited[i][j] && board[i][j] != 0) { // 빙산이고, 방문하지 않은 지역
queue.add(new int[]{i, j});
visited[i][j] = true;
year++;
if (year > 1) break;
while(!queue.isEmpty()) {
int[] cur = queue.poll();
int x = cur[0];
int y = cur[1];
int zero_count = 0;
for(int dir = 0; dir < 4; dir++){
int nx = x + dx[dir];
int ny = y + dy[dir];
if (nx < 0 || ny < 0 || nx >= n || ny >= m) continue;
if (visited[nx][ny]) continue;
if (board[nx][ny] == 0) zero_count++;
if (board[nx][ny] != 0) {
queue.add(new int[]{nx, ny});
visited[nx][ny] = true;
}
}
tmp[x][y] = zero_count;
}
}
}
}
cal(tmp);
}
public static void cal(int [][] tmp) {
for(int i = 0; i < n; i++){
for(int j = 0; j < m; j++){
board[i][j] -= tmp[i][j];
if (board[i][j] < 0) board[i][j] = 0;
}
}
}
public static boolean allMelt() {
for(int i = 0; i < n; i++)
for(int j = 0; j < m; j++){
if (board[i][j] != 0) return false;
}
return true;
}
}
회고
문제를 풀면서 빙산이 두 덩어리 이상으로 분리되는 조건을 생각하느라 좀 시간이 걸렸다.
기존에 생각했던 방식은 board 배열이 업데이트가 되고, 다시 bfs를 돌며 큐에 넣는 횟수가 2 이상인 경우를 메서드로 따로 뺐는데, 이렇게 하면 시간 낭비가 심할 것 같았다.
큐에 넣는 횟수가 2 이상인 경우를 왜 구하냐면, 방문하지 않고 빙산인 경우에만 큐에 넣어서 BFS를 돌려주고 있는데 만약 탐색이 끝나고 나서 다시 이중 포문을 돌 때 탐색을 진행한다면 이어져있지 않다는 의미가 된다. (말을 좀 어렵게 쓴듯...)
위에 생각했던 메서드로 따로 뺴서 BFS를 또 돌리는 형식 말고, BFS를 돌릴 때 같이 count를 계산한다면 해결되는 문제였다.
그리고 year > 1 이라는 탈출 조건을 넣어야 정상적인 답을 구할 수 있다.
제출하고 자꾸 56퍼에서 멈추고 시간초과가 나길래 봤더니 만일 전부 다 녹을 때까지 두 덩어리 이상으로 분리되지 않으면 프로그램은 0을 출력한다. 이 조건을 생각못했다. 그래서 allMelt() 라는 메서드를 통해서 체크해줬더니 잘 통과했다.
생각보다 그렇게 어렵지 않은 문제였다. 그런데 문제푸는 시간을 더 줄여야 할 것 같다.
특히, 해당 좌표에서 상하좌우 0의 갯수를 구할 때 board[nx][ny] != 0일 때, 즉 빙산일 때만 큐에 넣고 방문처리를 해줬어야 했는데 위 조건을 추가하지 않았더니 바닷물도 큐에 같이 넣어버려서 많이 꼬였다.
'Algorithm > 백준' 카테고리의 다른 글
[백준] Silver II. 촌수 계산 (0) | 2024.06.09 |
---|---|
[백준] Silver II. 연결 요소의 개수 (1) | 2024.06.06 |
[백준] Gold III. 감시 (0) | 2024.05.22 |
[백준] Gold IV. 연구소 (0) | 2024.05.21 |
N과 M 문제들 다시 풀어보기 (0) | 2024.05.14 |