1. 문제
2. 접근방법
일단 문제부터가 조금 이해 안됐었는데
왼쪽 오른쪽 번갈아가며 막대기를 던져서 날라가다 부딪힌 미네랄은 모두 파괴된다 해서
클러스터가 통채로 부서지나? 생각했는데
예제를 보며 확인하니
최초로 맞는 미네랄 딱 하나 만 부서지는 것이다.
이 문제는 bfs나 dfs를 쓰면서 시뮬레이션 구현하는 문제이다.
개념 자체는 어렵지 않지만 구현할 부분이 많다.
1.막대기를 좌우로 번갈아가며 높이에 맞춰 던져보고 최초로 맞는 미네랄은 없애고 //안맞으면 아무것도 안하고
2.없어진 미네랄 상하좌우에 있는 미네랄부터 bfs나 dfs를 돌며 그 클러스터가 바닥에 닿는지 검사하고
3. 하늘에 떠있는 클러스터가 발견 되면 그 클러스터를 통채로 떨구는데
4. 클러스터 안의 미네랄 중 하나라도 바닥을 만나거나 다른 클러스터를 만나면 그만 떨구기
나는 검사하는 작업은 bfs를 이용하고 bfs를 돌면서 bfs에 들어가는 모든 미네랄을 우선순위 큐에도 넣어뒀다.
//바닥과 가까운 순으로의 우선순위 큐
그러면 만약 검사를 했는데 하늘에 떠있는 클러스터라면
우선순위큐에서 그냥 하나씩 빼서 떨구면 되기 때문에 쉽게 구현 가능하다.
3. 풀이
1. 미네랄 위치들을 저장할 point 클래스 생성하고 comparable implements해오기
2. 맵 받고 던질거 위치들 받고 왼쪽 오른쪽 번갈아 가면서 미네랄 부수기
3. 부서진 위치의 상하좌우 중 방문하지 않았고 미네랄이 있는 곳이면 bfs쭉 돌려보고 바닥을 만나면 flag true하고 마져 다 돌리기 //돌리면서 만나는 미네랄 우선순위 큐에 다 넣어두기
4. flag가 false인 클러스터가 나오면
5. 맵 복사해서 우선순위 큐에서 하나씩 빼서 한칸씩 떨구고 떨어진 위치 다른 pq에 우선 저장
6. 큐 다 빠질때까지 문제 없이 돌리면 클러스터 한칸 내려온 것이니 복사한맵이랑 pq를 원본으로바꾸고 다시 5번으로
7. 내려오다 바닥이나 다른 미네랄을 만나면 복사본 폐기하고 원본으로 다시 2번으로
8. 다 던지고 나서의 맵 상태 출력
4. 자바 코드
package Gold;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.PriorityQueue;
import java.util.Queue;
import java.util.StringTokenizer;
public class P2933미네랄 {
static char map[][];
static int R, C;
static int dr[] = {0, -1, 0, 1};
static int dc[] = {-1, 0, 1, 0};
static boolean[][] visit;
static Queue<Point> que = new LinkedList<Point>(); // 검사용
static PriorityQueue<Point> pQue = new PriorityQueue<Point>(); // 떨구기용
static class Point implements Comparable<Point> {
int row, col;
public Point(int row, int col) {
super();
this.row = row;
this.col = col;
}
@Override
public int compareTo(Point o) {
return o.row - this.row; // 아랫줄 부터 ( row가 큰거부터)
}
}
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());
map = new char[R][C];
for (int i = 0; i < R; i++) {
String oneLine = br.readLine();
for (int j = 0; j < C; j++) {
map[i][j] = oneLine.charAt(j);
}
}
int N = Integer.parseInt(br.readLine());
st = new StringTokenizer(br.readLine());
int height[] = new int[N];
for (int i = 0; i < N; i++)
height[i] = Integer.parseInt(st.nextToken());
// 부순다.
// 부순위치 상하좌우에 미네랄있으면 거기부터 bfs시작 visit처리 해주면서
// 상 문제 없으면 좌 문제 없으면 우 다 검사하고 검사한것 중에 바닥 없으면 떨어뜨리게 클러스터 묶기
// 클론한 맵에 맨밑줄부터 한칸씩 떨구기 우선순위큐로 한칸씩 떨궈
// 내려갈라는데 바닥이나 다른 미네랄이면 종료
boolean L = true;
for (int i = 0; i < N; i++) { // 모든 던지기에 대해
Point hitPoint = hit(height[i], L); // 일단 때려보고
if (hitPoint == null) { // 안맞았으면 아무일도 안일어나기
L = !L;
continue;
}
visit = new boolean[R][C];
for (int j = 0; j < 4; j++) {
int row = hitPoint.row + dr[j]; // 맞은 위치 4방에 대해
int col = hitPoint.col + dc[j];
if (row < 0 || col < 0 || row >= R || col >= C)// 맵넘기지 말고
continue;
if (map[row][col] != 'x') // x가 아닌곳은 필요없고
continue;
if (visit[row][col]) // 이미 방문되어졌으면 필요없고
continue;
if (check(new Point(row, col)))// 바닥에 닿는지 쭉 검사해보자 연결된 클러스터는 다 visit되었겠지
continue; // 바닥에 잘 닿으면 떨굴일 없으니 넘기고
// 위를 다 지나왔으면 떨굴일이 생긴것
while (true) { // 끝에 다다를때까지 떨궈
if (!drop())
break;
}
// 떨굴일 한번뿐이랬으니
break;
}
L = !L; // 반대편 얘가 던지기
}
// 던지기 다 끝났으면 출력
for (int i = 0; i < R; i++) {
for (int j = 0; j < C; j++) {
System.out.print(map[i][j]);
}
System.out.println();
}
}
static Point hit(int height, boolean L) {
int row = R - height;
if (L) {
for (int i = 0; i < C; i++) {
if (map[row][i] == 'x') {
map[row][i] = '.';
return new Point(row, i);
}
}
}
else
for (int i = C - 1; i >= 0; i--)
if (map[row][i] == 'x') {
map[row][i] = '.';
return new Point(row, i);
}
return null;
}
static boolean check(Point np) {
boolean flag = false;
que.offer(np);
pQue.offer(np); // 나중에 떨굴일 생기면 쓰게 미리 넣어두기
visit[np.row][np.col] = true;
while (!que.isEmpty()) {
Point now = que.poll();
if (now.row == R - 1) {// 바닥에 닿네?
flag = true;
}
for (int i = 0; i < 4; i++) {
int row = now.row + dr[i];
int col = now.col + dc[i];
if (row < 0 || col < 0 || row >= R || col >= C)
continue;
if (visit[row][col])
continue;
if (map[row][col] != 'x')
continue;
visit[row][col] = true;
que.offer(new Point(row, col));
pQue.offer(new Point(row, col));
}
}
if (flag) {// 바닥에 닿았으니
pQue.clear(); // 떨어뜨릴거 없어 필요없어
return true;
}
return false; // 다 돌았는데 바닥에 안닿았으니 떨궈야지
}
static boolean drop() {
PriorityQueue<Point> npQue = new PriorityQueue<Point>(); // 한칸씩 떨구고 다음 떨굴 상태 저장하자
char [][] nMap = new char[R][C];
for(int i = 0 ; i < R ; i++) {
for(int j = 0 ; j < C ; j++) { //2차원 배열 깊은 복사
nMap[i][j] = map[i][j];
}
}
while (!pQue.isEmpty()) { // 다 떨굴때까지
Point now = pQue.poll(); // 밑에 줄꺼 부터 나올거에여
int row = now.row;
int col = now.col;
if (row + 1 >= R) { // 바닥이다? 끝
pQue.clear();
return false;
}
if (nMap[row + 1][col] == 'x') { // 떨굴라는데 미네랄이 있다? 끝
pQue.clear();
return false;
}
nMap[row][col] = '.'; // 원래 자리는 비워주고
nMap[row + 1][col] = 'x'; // 아래 자리는 채워주고
npQue.offer(new Point(row + 1, col)); // 또 떨구기 위해
}
// 다 잘 떨궈졌으면 원본들 변화
pQue = npQue;
for(int i = 0 ; i < R ; i++) {
for(int j = 0 ; j < C ; j++) { //2차원 배열 깊은 복사
map[i][j] = nMap[i][j];
}
}
return true;
}
}
5. 마치며
사실 구현이 어려운 문제라 그렇지 딱히 설명할 거리가 없다.
하나 하나 차근차근 해가다 보면 이상한데서 문제가 생겨서 아오 ㅁㄴㅇㄹ
나는 떨구다가 바닥이나 미네랄 만나면 pQue를 비워줬어야하는데 그 작업을 안했다가
몇 시간을 삽질 했는지 모르겠다;;
뭔가 맞은것 같은데 안돼도 문제는 문제가 없고 우리의 코드가 문제니 다시 천천히 잘 봐보도록 하자
'알고리즘 문제풀이 > 백준' 카테고리의 다른 글
[백준] 16174 점프왕쩰리(Large) (0) | 2020.09.04 |
---|---|
[백준] 17472 다리만들기2 (0) | 2020.09.04 |
[백준] 19535 ㄷㄷㄷㅈ (0) | 2020.08.31 |
[백준] 18513 샘터 (0) | 2020.08.31 |
[백준] 17471 게리멘더링 (0) | 2020.08.28 |