[백준] 3197번 - 백조의호수 (자바, java)
by 코딩무비문제
https://www.acmicpc.net/problem/3197
3197번: 백조의 호수
입력의 첫째 줄에는 R과 C가 주어진다. 단, 1 ≤ R, C ≤ 1500. 다음 R개의 줄에는 각각 길이 C의 문자열이 하나씩 주어진다. '.'은 물 공간, 'X'는 빙판 공간, 'L'은 백조가 있는 공간으로 나타낸다.
www.acmicpc.net
문제 설명
두 백조가 며칠 만에 만날 수 있는지 찾는 문제입니다.
빙하가 존재하고 매일 물과 맞닿는 부분은 물로 변하게 됩니다.
주의할 점
1. 백조는 물 위에 존재한다.
2. 백조A와 백조 B가 만날 수 있는 최소 시간이다. (백조 사이의 최소 거리가 아님)
문제풀이
2가지 방법으로 접근할 수 있습니다.
1. BFS
2. union-find(문제 해결 방법에 대해서만 설명하겠습니다.)
1. BFS
우리는 문제에서 주어진 것을 다음과 같은 반복문을 통하여 해결할 수 있습니다.
1. 두 백조가 만날 수 있는지 확인
- 만날 수 있다면 day 리턴하고 반복문 빠져나오기
2. 빙하 녹이기
3. day++
while (true) {
    // 백조 인접 여부 확인
    if (check()) {
        System.out.println(day);
        break;
    }
    //빙하 녹이기(물 확산)
    melt();
    day++;
}
1. 두 백조가 만날 수 있는지 확인
문제 난이도를 보면 알 수 있듯이 단순히 BFS을 이용하면 해결할 수 없습니다.
그 이유는 데이터의 크기를 보면 알 수 있습니다.
그래프의 크기는 1500*1500입니다.
만약 백조가 양쪽 끝에 있다면 약 1000번 만에 만날 수 있습니다.
따라서 연산 횟수는 1500*1500*1000 => 22억 5천 => 22초(시간초과)
그래서 우리는 조금 더 이를 단순화할 필요가 있습니다.
어떻게 단순화하냐면. BFS을 돌릴 때 만난 빙하를 큐에 넣는다.
다음 BFS은 빙하가 들어있는 큐를 기준으로 BFS을 돌린다.
Queue<Node> newQ = new ArrayDeque<>();
while (!Q.isEmpty()) {
    int size = Q.size();
    while (size-- > 0) {
        Node cur = Q.poll();
        int x = cur.x;
        int y = cur.y;
        if (cur.x == end.x && cur.y == end.y) {
            return true;
        }
        for (int i = 0; i < 4; i++) {
            int nx = x + dx[i];
            int ny = y + dy[i];
            if (isOut(nx, ny) || visited[nx][ny]) {
                continue;
            }
            visited[nx][ny] = true;
            if (G[nx][ny] == 'X') {
                newQ.add(new Node(nx, ny));
                continue;
            }
            Q.add(new Node(nx, ny));
        }
    }
}
Q = newQ;
2. 빙하 녹이기
빙하는 상하좌우로 물이 있을 경우 녹습니다. 따라서 처음에 물을 큐 water에 넣고 상하좌우에 물이 있으면 새로운 큐 newQ에 넣습니다. newQ는 다음 날 가장자리에 있는 물이 됩니다.
static void melt() {
    Queue<Node> melting = new ArrayDeque<>();
    while (!water.isEmpty()) {
        Node cur = water.poll();
        int x = cur.x;
        int y = cur.y;
        for (int i = 0; i < 4; i++) {
            int nx = x + dx[i];
            int ny = y + dy[i];
            if (isOut(nx, ny) || G[nx][ny] != 'X') {
                continue;
            }
            G[nx][ny] = '.';
            melting.add(new Node(nx, ny));
        }
    }
    water = melting;
}
전체코드
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.*;
public class Main {
    static class Node {
        int x;
        int y;
        public Node(int x, int y) {
            this.x = x;
            this.y = y;
        }
    }
    // 백조 인접 여부
    static boolean check() {
        Queue<Node> newQ = new ArrayDeque<>();
        while (!Q.isEmpty()) {
            int size = Q.size();
            while (size-- > 0) {
                Node cur = Q.poll();
                int x = cur.x;
                int y = cur.y;
                if (cur.x == end.x && cur.y == end.y) {
                    return true;
                }
                for (int i = 0; i < 4; i++) {
                    int nx = x + dx[i];
                    int ny = y + dy[i];
                    if (isOut(nx, ny) || visited[nx][ny]) {
                        continue;
                    }
                    visited[nx][ny] = true;
                    if (G[nx][ny] == 'X') {
                        newQ.add(new Node(nx, ny));
                        continue;
                    }
                    Q.add(new Node(nx, ny));
                }
            }
        }
        Q = newQ;
        return false;
    }
    static boolean isOut(int x, int y) {
        if (x < 0 || y < 0 || x >= R || y >= C) {
            return true;
        }
        return false;
    }
    static void melt() {
        Queue<Node> melting = new ArrayDeque<>();
        while (!water.isEmpty()) {
            Node cur = water.poll();
            int x = cur.x;
            int y = cur.y;
            for (int i = 0; i < 4; i++) {
                int nx = x + dx[i];
                int ny = y + dy[i];
                if (isOut(nx, ny) || G[nx][ny] != 'X') {
                    continue;
                }
                G[nx][ny] = '.';
                melting.add(new Node(nx, ny));
            }
        }
        water = melting;
    }
    static Node start, end;
    static Queue<Node> Q;
    static Queue<Node> water;
    static int[] dx = {-1, 1, 0, 0};
    static int[] dy = {0, 0, -1, 1};
    static boolean[][] visited;
    static int R, C;
    static char[][] G;
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());
        R = Integer.parseInt(st.nextToken());
        C = Integer.parseInt(st.nextToken());
        visited = new boolean[R][C];
        G = new char[R][C];
        water = new ArrayDeque<>();
        Q = new ArrayDeque<>();
        for (int i = 0; i < R; i++) {
            G[i] = br.readLine().toCharArray();
            for (int j = 0; j < C; j++) {
                if (G[i][j] == 'L') {
                    if (start == null) {
                        start = new Node(i, j);
                        visited[i][j] = true;
                        Q.add(new Node(i, j));
                    } else {
                        end = new Node(i, j);
                    }
                    water.add(new Node(i, j));
                } else if (G[i][j] == '.') {
                    water.add(new Node(i, j));
                }
            }
        }
        int day = 0;
        while (true) {
            // 백조 인접 여부 확인
            if (check()) {
                System.out.println(day);
                break;
            }
            //빙하 녹이기(물 확산)
            melt();
            day++;
        }
    }
}
결과

2. union-find을 이용한 문제 예시
주어진 3번 예시를 다음과 같이 볼 수 있습니다.
각각의 물이 뭉쳐 있는 집합을 이용하여
union-find(disjoint-set)을 이용하여 문제를 해결할 수 있습니다.
A백조와 B백조가 같은 area 번호라면 둘은 만날 수 있습니다.


'문제풀이(ps)' 카테고리의 다른 글
| LeetHub 디렉토리 구조 백준허브처럼 변경하기 (4) | 2023.07.26 | 
|---|---|
| [프로그래머스] SQL 코테 정복하기 (14) | 2022.04.29 | 
| [백준] 17070번 파이프 옮기기 1 (python 파이썬) (10) | 2022.04.28 | 
| [백준]11049번 행렬 곱셈 순서(python 파이썬) (18) | 2022.04.26 | 
| [백준] 12865번 평범한 배낭(python 파이썬) (6) | 2022.04.25 | 
블로그의 정보
코딩무비
코딩무비