동굴 내에서 막대 던지기를 한다. 막대는 미네랄을 파괴할 수도 있다.
R행 C열의 동굴. 각 칸은 빈칸 혹은 미네랄. 네 방향 중 하나로 인접한 미네랄 = 같은 클러스터
번갈아가며 막대를 던진다. 막대를 던지기 전에 던질 높이를 정한다. 막대는 땅과 수평을 이루며 날아간다.
날아가는 도중에 미네랄을 만나면 그 칸의 미네랄은 모두 파괴되며 막대는 그 자리에서 멈춘다.
클러스터라는 개념이 등장한다. 미네랄이 파괴된 이후 남은 클러스터가 분리될 수 있다고 하는데 이 부분부터 이해가 필요하다.
지도가 2차원 평면을 나타내는 것이 아니라 세로가 높이를 나타내고 있다.
막대를 던지는 높이는 1 ~ R 사이로 주어지며 이를 통해서 미네랄이 상하로 갈라지는 것이다.
필요한 기능
- 턴을 구분한다.→ height를 순회하면서 %2 == 0 이면 left → right이다.
- 막대를 던지는 기능 → height가 r이 돼서 거기부터 c를 증가시켜 가며 처음 미네랄을 만나는 곳을 찾는다.
- 미네랄 클러스터 판단 클러스터를 확인할 수 있어야 한다. 클러스터가 하강하는 경우 = 바닥과 연결이 안 돼 있다.
- 미네랄과 충돌할 시 해당 위치의 4방향 기준 시작점으로 추가한 뒤 탐색을 시작
- 탐색 중 바닥과 연결되지 않은 것이 하강하는 클러스터
- 하강할 클러스터를 어떻게 하강시키는가? 떨어지는 동안 클러스터의 모양이 유지된다.
- 떨어지다가 바닥에 도달하지 않았는데 다른 미네랄에 걸리는 경우
- 걸리지 않고 가장 아래 부분이 바닥에 도달하는 경우
package boj;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Queue;
import java.util.StringTokenizer;
public class BOJ2933 {
static final int[][] DIRECTION = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}}; //상하좌우
static FastReader scan = new FastReader();
static int R, C, N;
static char[][] initialMap;
static int[] heights;
static class Result {
boolean isDown;
List<int[]> cand;
public Result(boolean isDown, List<int[]> cand) {
this.isDown = isDown;
this.cand = cand;
}
}
static void input() {
R = scan.nextInt();
C = scan.nextInt();
initialMap = new char[R][C];
for (int i = 0; i < R; i++) {
initialMap[i] = scan.next().toCharArray();
}
N = scan.nextInt();
heights = new int[N];
for (int i = 0; i < N; i++) {
heights[i] = R - scan.nextInt(); // 높이
}
}
static void print(char[][] map) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < map.length; i++) {
sb.append(map[i]);
if (i != map.length -1) {
sb.append("\\n");
}
}
System.out.println(sb);
}
static void solve(char[][] map) {
for (int i = 0; i < N; i++) {
int height = heights[i];
int[] target;
if (i % 2 == 0) {
target = findMineral(map, height, 0);
} else {
// right to left
target = findMineral(map, height, 1);
}
if (target == null) {
continue; // 맞은 미네랄이 없다.
}
map[target[0]][target[1]] = '.';
// 해당 타겟 기중 상하좌우를 보고 하강 클러스터 후보 찾기
List<int[]> cand = new ArrayList<>();
for (int d = 0; d < 4; d++) {
int nr = target[0] + DIRECTION[d][0];
int nc = target[1] + DIRECTION[d][1];
if (!inRange(nr, nc)) {
continue;
}
if (map[nr][nc] == 'x') {
cand.add(new int[]{nr, nc});
}
}
if (cand.isEmpty()) {
// 상하좌우에 연결된 미네랄이 없다 -> 클러스터 후보가 없다.
continue;
}
List<List<int[]>> clusters = new ArrayList<>();
boolean[][] visited = new boolean[R][C];
for (int[] mineral : cand) {
if (!visited[mineral[0]][mineral[1]]) {
Result result = findDownCluster(map, mineral);
if (result.isDown) {
clusters.add(result.cand);
}
for (int[] pos : result.cand) {
visited[pos[0]][pos[1]] = true;
}
}
}
for (List<int[]> cluster : clusters) {
for (int[] pos : cluster) {
map[pos[0]][pos[1]] = '.';
}
int minHeight = calcDownHeight(cluster, map);
for (int[] pos : cluster) {
map[pos[0] + minHeight][pos[1]] = 'x';
}
}
}
print(map);
}
static int calcDownHeight(List<int[]> cluster, char[][] map) {
int minHeight = Integer.MAX_VALUE;
for (int[] pos : cluster) {
int r = pos[0];
int c = pos[1];
int h = 0;
r++;
while (inRange(r, c) && map[r][c] != 'x') {
h++;
r++;
}
minHeight = Math.min(minHeight, h);
}
return minHeight;
}
static Result findDownCluster(char[][] map, int[] start) {
Queue<int[]> q = new ArrayDeque<>();
boolean[][] visited = new boolean[R][C];
q.add(start);
visited[start[0]][start[1]] = true;
List<int[]> cand = new ArrayList<>();
boolean isBottom = false;
while (!q.isEmpty()) {
int[] curr = q.poll();
cand.add(curr);
int r = curr[0];
int c = curr[1];
if (r == R - 1) {
// 밑바닥 도달
isBottom = true;
}
for (int d = 0; d < 4; d++) {
int nr = r + DIRECTION[d][0];
int nc = c + DIRECTION[d][1];
if (!inRange(nr, nc)) continue;
if (visited[nr][nc]) continue;
if (map[nr][nc] == 'x') {
q.add(new int[]{nr, nc});
visited[nr][nc] = true;
}
}
}
return isBottom ? new Result(false, List.of()) : new Result(true, cand);
}
static boolean inRange(int r, int c) {
return r >= 0 && r < R && c >= 0 && c < C;
}
static int[] findMineral(char[][] map, int r, int dir) {
if (dir == 0) {
// left to right
for (int i = 0; i < C; i++) {
if (map[r][i] == 'x') {
return new int[]{r, i};
}
}
} else {
for (int i = C - 1; i >= 0; i--) {
if (map[r][i] == 'x') {
return new int[]{r, i};
}
}
}
return null;
}
public static void main(String[] args) {
input();
solve(copy(initialMap));
}
private static char[][] copy(char[][] initialMap) {
return Arrays.stream(initialMap)
.map(char[]::clone)
.toArray(char[][]::new);
}
static class FastReader {
BufferedReader br;
StringTokenizer st;
public FastReader() {
br = new BufferedReader(new InputStreamReader(System.in));
}
String next() {
while (st == null || !st.hasMoreTokens()) {
try {
st = new StringTokenizer(br.readLine());
} catch (IOException e) {
e.printStackTrace();
}
}
return st.nextToken();
}
int nextInt() {
return Integer.parseInt(next());
}
}
}
'Algorithm > BOJ' 카테고리의 다른 글
BOJ 10021 - Watering the Fields (0) | 2024.11.06 |
---|---|
BOJ 2292 - 벌집 (5) | 2024.11.06 |
BOJ 15591 - MooTube (Silver) (0) | 2024.10.31 |
BOJ 10830 - 행렬의 제곱 (0) | 2024.10.31 |
BOJ 12865 - 평범한 배낭 (0) | 2024.10.31 |