백준 13460 - 구슬 탈출2

Updated:

Java

13460 번 - 구슬 탈출2

문제

구슬 탈출은 직사각형 보드에 빨간 구슬과 파란 구슬을 하나씩 넣은 다음, 빨간 구슬을 구멍을 통해 빼내는 게임이다.
보드의 세로 크기는 N, 가로 크기는 M
왼쪽으로 기울이기, 오른쪽으로 기울이기, 위쪽으로 기울이기, 아래쪽으로 기울이기와 같은 네 가지 동작으로 구슬을 빼내야 한다.
빨간 구슬이 구멍에 빠지면 성공이지만, 파란 구슬이 구멍에 빠지면 실패이다.

접근 방법

  1. 상하좌우 기울이는 방법을 10번 세팅한다. 이는 4^10 == 1,048,576
  2. 구슬을 이동시킨다.
  3. 만약 상/하 일때 B,R이 같은 x축에 있으면, y값 비교하여 둘 중 어느 공을 먼저 이동할지 정한다, 좌/우 일대 B,R이 같은 y축에 있으면 x값 비교
  4. 구멍을 만나면 종료하고, 몇번 방문했는지 기록한다.

코드

import java.util.*;
import java.io.*;

public class Main {
	static int n, m, result = Integer.MAX_VALUE;
	static char[][] board;
	static int[] dir;
	static int Ry, Rx, By, Bx;
	public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    	StringTokenizer stk = new StringTokenizer(br.readLine());
    	n = stoi(stk.nextToken());
    	m = stoi(stk.nextToken());
    	board = new char[n][m];
    	dir = new int[10];
    	
    	for(int i = 0; i < n; i++) {
    		String str = br.readLine();
    		for(int j = 0; j < m; j++) {
    			board[i][j] = str.charAt(j);
    			if(board[i][j] == 'B') {
    				By = i;
    				Bx = j;
    			}
    			else if(board[i][j] == 'R') {
    				Ry = i;
    				Rx = j;
    			}
    		}
    	}
    	
    	DFS(0);
    	
    	if(result == Integer.MAX_VALUE) 
    		System.out.println(-1);
    	else
    		System.out.println(result);
    	br.close();
	}
	// 총 10번의 어떻게 기울일 것인가 DFS로 모두 확인한다.
	static void DFS(int lv) {
		if(lv == 10) {
			char[][] nBoard = new char[n][m];
			for(int i = 0; i < n; i++) {
				nBoard[i] = board[i].clone();
			}
			
			int rst = Move(nBoard);
			if(rst != -1) {
				result = Math.min(result, rst);
			}
			return;
		}
		for(int i = 0; i < 4; i++) {
			dir[lv] = i;
			DFS(lv+1);
		}
	}
	// 우 하 좌 상
	static int[] dy = {0, 1, 0, -1};
	static int[] dx = {1, 0, -1, 0};
	
	static int Move(char[][] mBoard) {
		int curD;
		int by = By, bx = Bx, ry = Ry, rx = Rx;
		int y,x;	// 처음 공 위치
		boolean bH = false, rH = false;
		boolean isRedFirst = true;
		
		// 10번 각 방향으로 기우린다.
		for(int i = 0; i < 10; i++) {
			curD = dir[i];	// 상 우 하 좌로 기운다.
			// 상, 하로 이동하며 R,B가 같은 위치  x에 있으면
			if((curD == 1 || curD == 3) && bx == rx) {
				// 위로 올라가는 경우
				if(curD == 3) {
					if(ry < by)			// 빨간 공이 더 위에 있을 시
						isRedFirst = true;
					else
						isRedFirst = false;
				}
				// 아래로 내려가는 경우
				else {
					if(ry > by)			// 빨간 공이 더 아래에 있을 때
						isRedFirst = true;
					else
						isRedFirst = false;
				}
			}
			// 좌, 우로 이동하며 R,B가 같은 위치 y에 있으면
			else if((curD == 0 || curD == 2) && by == ry) {
				// 우로 이동하는 경우
				if(curD == 0) {
					if(rx > bx)			// 빨간 공이 오른쪽에 있을 시
						isRedFirst = true;
					else
						isRedFirst = false;
				}
				// 좌로 이동하는 경우
				else {
					if(rx < bx)			// 빨간 공이 더 왼쪽에 있을 시
						isRedFirst = true;
					else
						isRedFirst = false;
				}
			}
			
			// 빨간 공 혹은 파란 공을 굴린다.
			for(int idx = 0; idx < 2; idx++) {
				if(isRedFirst) {
					// 빨간 공을 굴리기 시작			
					y = ry;
					x = rx;
					while(true) {
						ry += dy[curD];
						rx += dx[curD];
						// 벽이나 다른 색의 공을 만나면
						if(!isIn(ry,rx) || mBoard[ry][rx] == 'B' || board[ry][rx] == '#') {
							ry -= dy[curD];
							rx -= dx[curD];
							mBoard[y][x] = '.';
							mBoard[ry][rx] = 'R';
							break;
						}
						// 구멍에 빠졌을시
						if(board[ry][rx] == 'O') {
							rH = true;
							mBoard[y][x] = '.';
							break;
						}
					}
					isRedFirst = false;
				}
				else {
					// 파란 공을 굴리기 시작
					y = by;
					x = bx;
					while(true) {
						by += dy[curD];
						bx += dx[curD];
						// 벽이나 다른 색의 공을 만나면
						if(!isIn(by,bx) || mBoard[by][bx] == 'R' || board[by][bx] == '#') {
							by -= dy[curD];
							bx -= dx[curD];
							mBoard[y][x] = '.';
							mBoard[by][bx] = 'B';
							break;
						}
						// 구멍에 빠졌을시
						if(mBoard[by][bx] == 'O') {
							bH = true;
							mBoard[y][x] = '.';
							break;
						}
					}
					isRedFirst = true;
				}
			}
			
			// 파란공이 빠지면 바로 종료
			if(bH) {
				return -1;
			}
			// 빨간 공이 만나면, 종료하고 몇번 기울렸는지 횟수를 기록
			if(rH) {
				return i + 1;
			}
		}
		return -1;
	}
	
	public static boolean isIn(int y, int x){
        if(y < 0 || y == n || x < 0 || x == m)
            return false;
        return true;
    }
	
	static int stoi(String str) {
    	return Integer.parseInt(str);
    }
}

총평

난이도

⭐⭐⭐★★

후기

구현 문제였으며, 반례를 통해 오류를 많이 찾은 것 외에는 큰 어려움이 없던 문제였다.

개선할 점