[백준] 18500. 미네랄 2

4 분 소요

문제 링크

[백준] 18500. 미네랄 2


풀이 과정

파괴된 미네랄로 인해 클러스터가 분리되면, 새로 파생된 클러스터가 공중에 떠 있을 수가 있습니다. 이 때, 공중에 떠있는 이 클러스터를 모양의 변화 없이 아래로 내려야 합니다. 모양을 유지하기 위해선 한 칸씩 내릴 수 있기에, 클러스터에 속한 모든 미네랄의 좌표를 알아야 할 필요가 있습니다. 또한, 파괴된 미네랄의 좌표가 아래 사진과 같을 때, 파괴된 미네랄의 아래 부분 역시 공중에 뜰 수 있기에 파괴된 미네랄의 상, 하, 좌, 우 를 모두 확인해 봐야 했습니다.

알고리즘은 다음과 같습니다.

  1. 해당 높이에 막대를 던지면 제일 처음 만나는 미네랄을 파괴합니다.
  2. 미네랄이 파괴되어 새로운 클러스터가 생성되면, 그 클러스터를 밑으로 내려야하는 지 확인합니다.
  3. 새로 생성된 클러스터가 공중에 떠 있게 되어 바닥으로 내려가야 된다면, 바닥에 맞닿을 때까지 한 칸씩 밑으로 내립니다.
  4. 위 과정을 반복합니다.


Point target = null;

if (n % 2 == 0) {
    for (int j = 0; j < C; j++) {
        if (map[i][j] == '.') continue;

        map[i][j] = '.';
        target = new Point(i, j);
        break;
    }
} else {
    for (int j = C - 1; j >= 0; j--) {
        if (map[i][j] == '.') continue;

        map[i][j] = '.';
        target = new Point(i, j);
        break;
    }
}

if (target == null) continue;

미네랄을 파괴하는 코드는 위와 같습니다. 막대가 날아가는 경로에 미네랄이 존재한다면, 미네랄을 파괴시킨 다음 빈 칸으로 둡니다. 또한 파괴된 좌표의 값을 변수 target 에 저장합니다.


Queue<Point> queue = new LinkedList<>();
for (int i = 0; i < R; i++)
    Arrays.fill(visit[i], false);

queue.add(next);
visit[next.x][next.y] = true;
cluster.add(next);

while (!queue.isEmpty()) {
    Point now = queue.poll();

    if (now.x == R - 1) {
        cluster.clear();
        break;
    }

    for (int dd = 0; dd < 4; dd++) {
        int ni = now.x + di[dd];
        int nj = now.y + dj[dd];

        if (!check(ni, nj) || visit[ni][nj] || map[ni][nj] == '.') continue;

        visit[ni][nj] = true;
        queue.add(new Point(ni, nj));
        cluster.add(new Point(ni, nj));
    }
}

findCluster 메소드는 미네랄 파괴로 인해 분리된 클러스터가 있는 지 확인합니다. 상, 하, 좌, 우 방향으로 bfs 탐색을 실행하며, 클러스터로 묶여 있는 미네랄들의 좌표를 cluster 리스트에 담습니다.


if (now.x == R - 1) {
    cluster.clear();
    break;
}

이 때, 클러스터를 이루는 어느 미네랄의 좌표의 행이 R-1 에 해당하면, 이는 클러스터가 바닥에 닿아있음을 의미하므로, 공중에 떠있지 않은 클러스터임을 확인할 수 있습니다. 해당 클러스터는 공중에 떠 있지 않으므로(바닥으로부터 연결되므로), 클러스터를 내리는 과정을 생략합니다.


if (p.x == R - 1 || (p.x + 1 <= R - 1 && map[p.x + 1][p.y] == 'x' && !cluster.contains(new Point(p.x + 1, p.y)))) {
    bottom = true;
}

down 메소드는 클러스터를 바닥으로 내리는 작업을 실행합니다. 현재 좌표가 바닥이거나, 한 칸을 내렸을 때 그 좌표가 다른 클러스터에 속해 있다면 내리는 작업을 그만합니다.


코드

import java.awt.*;
import java.util.*;
import java.util.List;

public class Main {
    static int R, C, N;
    static char[][] map;
    static boolean[][] visit;
    static List<Point> cluster;

    static int[] di = {-1, 1, 0, 0};
    static int[] dj = {0, 0, -1, 1};

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        R = sc.nextInt();
        C = sc.nextInt();
        map = new char[R][C];
        visit = new boolean[R][C];
        cluster = new ArrayList<>();

        for (int i = 0; i < R; i++) {
            map[i] = sc.next().toCharArray();
        }

        N = sc.nextInt();
        for (int n = 0; n < N; n++) {
            int height = sc.nextInt();
            int i = R - height;

            Point target = null;

            if (n % 2 == 0) {
                for (int j = 0; j < C; j++) {
                    if (map[i][j] == '.') continue;

                    map[i][j] = '.';
                    target = new Point(i, j);
                    break;
                }
            } else {
                for (int j = C - 1; j >= 0; j--) {
                    if (map[i][j] == '.') continue;

                    map[i][j] = '.';
                    target = new Point(i, j);
                    break;
                }
            }

            if (target == null) continue;

            findCluster(target);
        }

        for (int i = 0; i < R; i++) {
            for (int j = 0; j < C; j++) {
                System.out.print(map[i][j]);
            }
            System.out.println();
        }
    }

    static void findCluster(Point target) {
        for (int d = 0; d < 4; d++) {
            cluster.clear();

            Point next = new Point(target.x + di[d], target.y + dj[d]);

            if (!check(next.x, next.y)) continue;

            if (map[next.x][next.y] == 'x') {
                Queue<Point> queue = new LinkedList<>();
                for (int i = 0; i < R; i++)
                    Arrays.fill(visit[i], false);

                queue.add(next);
                visit[next.x][next.y] = true;
                cluster.add(next);

                while (!queue.isEmpty()) {
                    Point now = queue.poll();

                    if (now.x == R - 1) {
                        cluster.clear();
                        break;
                    }

                    for (int dd = 0; dd < 4; dd++) {
                        int ni = now.x + di[dd];
                        int nj = now.y + dj[dd];

                        if (!check(ni, nj) || visit[ni][nj] || map[ni][nj] == '.') continue;

                        visit[ni][nj] = true;
                        queue.add(new Point(ni, nj));
                        cluster.add(new Point(ni, nj));
                    }
                }

                if (!cluster.isEmpty())
                    down();
            }
        }
    }

    static void down() {
        while (true) {
            boolean bottom = false;

            for (Point p : cluster) {
                if (p.x == R - 1 || (p.x + 1 <= R - 1 && map[p.x + 1][p.y] == 'x' && !cluster.contains(new Point(p.x + 1, p.y)))) {
                    bottom = true;
                }
            }

            if (bottom) break;

            for (Point p : cluster) {
                map[p.x][p.y] = '.';
            }

            for (Point p : cluster) {
                map[p.x + 1][p.y] = 'x';
                p.x++;
            }
        }
    }

    static boolean check(int i, int j) {
        if (i < 0 || i > R - 1 || j < 0 || j > C - 1) return false;
        return true;
    }
}

카테고리:

업데이트:

댓글남기기