PS/백준
백준 - 23634 미안하다 이거 보여주려고 어그로 끌었다(JAVA)
PSW
2024. 7. 22. 15:56
bfs + disjoint_set을 이용한 문제로 개인적으로 어렵게 느껴져서 올리고자 한다.
https://www.acmicpc.net/problem/23634
설명
N X M의 0,1,2로 이루어진 지도가 주어진다(0 = 불,나무 = 1, 돌 = 2)
하루동안 불은 상하좌우로 옮겨붙으면서 나무를 태우고 불이 합쳐 질떄 크기의 합과 그때의 일 수를 출력한다.
접근
처음에는 https://www.acmicpc.net/problem/14868 이 문제와 같은 문제인지 알았는데 기본적으로 같지만 문제 설명에서 "불이 돌에 막혀 모든 불이 하나로 합쳐지지 않을 수도 있는데, 그럴 땐 합쳐질 수 있는 모든 불이 합쳐지는 최소 시간과 그때의 불의 크기의 합을 구하면 돼."이 문장으로 더 어려워진 문제이다 P4문제 이지만 개인적으로 문명보다 더어려웠다.위 문제를 먼저 풀어보기를 권장한다.
처음에는 무슨말인지 이해가 안갔지만 다음 테스트케이스를 보고 이해가 됐다.
5 6
011112
201120
022211
111101
111111
다음 예제를 보면 돌로인해서 불이 모두 합쳐질 수 가 없는걸 볼 수 있다.
돌로 나누어진그룹에 번호를매겨서 HashMap을 이용해 각 그룹의 불의 개수를 세어서 put을 해주고,
일 수가 지날떄마다 각 그룹의 불이 모두 만날떄 break를 해주면 해결 가능하다.
코드
package bj미안하다이거보여주려고어그로끌었다_23634;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;
public class bj미안하다이거보여주려고어그로끌었다_23634 {
private static int N, M, answer, count;
private static int[][] board, group;
private static boolean[][] visited;
private static int[] parent, size;
private static int[] dx = {-1, 1, 0, 0}, dy = {0, 0, -1, 1};
private static Queue<Node> queue = new ArrayDeque<>();
private static HashMap<Integer, Integer> map = new HashMap<>();
private static List<Node> 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());
board = new int[N][M];
visited = new boolean[N][M];
parent = new int[N * M];
size = new int[N * M];
group = new int[N][M];
for (int i = 0; i < N * M; i++) {
parent[i] = i;
size[i] = 1;
}
for (int i = 0; i < N; i++) {
String s = br.readLine();
for (int j = 0; j < M; j++) {
board[i][j] = s.charAt(j) - '0';
if (board[i][j] == 0) count++;
}
}
init();
int region = 1;
for (int i = 0; i < N; i++) {
for (int j = 0; j < M; j++) {
if (board[i][j] == 0 && !visited[i][j]) {
queue.add(new Node(i, j));
group[i][j] = region;
visited[i][j] = true;
list.add(new Node(i, j));
grouping(region++);
}
}
}
visited = new boolean[N][M];
for (int i = 0; i < N; i++) {
for (int j = 0; j < M; j++) {
if (board[i][j] == 0) {
queue.add(new Node(i, j));
visited[i][j] = true;
}
}
}
while (!tc()) {
bfs();
answer++;
}
if (count == 0) System.out.println(0 + " " + 0);
else {
int sum = 0;
for (Node node : list) {
int i = node.x * M + node.y;
sum += size[findParent(i)];
}
System.out.println(answer + " " + sum);
}
}
private static boolean tc() {
for (Node node : list) {
int n = node.x * M + node.y;
int region = group[node.x][node.y];
if (size[findParent(n)] != map.get(region)) return false;
}
return true;
}
private static void grouping(int region) {
int count = 0;
while (!queue.isEmpty()) {
Node cur = queue.poll();
int x = cur.x;
int y = cur.y;
if (board[x][y] == 0) count++;
for (int i = 0; i < 4; i++) {
int nx = x + dx[i];
int ny = y + dy[i];
if (nx < 0 || nx >= N || ny < 0 || ny >= M || board[nx][ny] == 2 || visited[nx][ny]) continue;
group[nx][ny] = region;
visited[nx][ny] = true;
queue.add(new Node(nx, ny));
}
}
map.put(region, count);
}
private static void bfs() {
int size = queue.size();
for (int i = 0; i < size; i++) {
Node cur = queue.poll();
int x = cur.x;
int y = cur.y;
int cnt = 0;
for (int j = 0; j < 4; j++) {
int nx = x + dx[j];
int ny = y + dy[j];
if (nx < 0 || nx >= N || ny < 0 || ny >= M || board[nx][ny] != 1 || visited[nx][ny]) continue;
board[nx][ny] = 0;
cnt++;
int a = x * M + y;
int b = nx * M + ny;
visited[nx][ny] = true;
union(a, b);
for (int k = 0; k < 4; k++) {
int nnx = nx + dx[k];
int nny = ny + dy[k];
if (nnx >= 0 && nnx < N && nny >= 0 && nny < M && board[nnx][nny] == 0) union(b, nnx * M + nny);
}
queue.add(new Node(nx, ny));
count++;
}
int num = map.get(group[x][y]) + cnt;
map.replace(group[x][y], num);
}
}
private static void init() {
for (int i = 0; i < N; i++) {
for (int j = 0; j < M; j++) {
if (board[i][j] == 0) {
for (int k = 0; k < 4; k++) {
int nx = i + dx[k];
int ny = j + dy[k];
if (nx < 0 || nx >= N || ny < 0 || ny >= M || board[nx][ny] != 0) continue;
int a = i * M + j;
int b = nx * M + ny;
union(a, b);
}
}
}
}
}
private static void union(int a, int b) {
int ap = findParent(a);
int bp = findParent(b);
if (ap != bp) {
parent[bp] = ap;
size[ap] += size[bp];
}
}
private static int findParent(int x) {
if (parent[x] == x) return x;
else return parent[x] = findParent(parent[x]);
}
private static class Node {
int x, y;
public Node(int x, int y) {
this.x = x;
this.y = y;
}
}
}
후기
https://www.acmicpc.net/problem/14868 이문제는 항상 모든 불이 하나로 합쳐지지만
23634는 돌로 막혀 그룹이 나누어 질 수 있어서 더어려웠고 코드도 더 길어진거같다.