[java,백준13460]구슬 탈출 2 - BFS와 DFS
BFS와 DFS(중복순열) 둘 다 구할 수 있고, 벽 끝까지 이동하는 빨간 구슬을 구멍으로 빼내는 최소 이동 횟수를 구하는 문제입니다.
1가지 예시를 들자면, “#…RB#” 일 때 왼쪽 기울이기를 1번 한다면? “#RB…#” 이 된다. 왜??
- ”.” 은 빈 공간이라 지나가고 “#”은 벽이라 멈추고 마지막은 구슬끼리 부딪혀서 멈추기 때문!
- R, B를 같이 움직이게 하거나 R, B를 따로 움직여도 상관없음! 겹치면 다시 백 하면 되니까!
풀이
구현 시 주의사항
💡 구슬이 동시에 움직이는 상황과 구슬이 겹치는 경우를 처리할 때 실수하기 쉽습니다. 각 방향별로 구슬의 우선순위를 정확히 파악해야 한다.
=> 움직일 때 마다 디버깅 추천!! (배열 출력해보아라)
💡 bfs(or dfs) -> move 함수 형태로 구현한다.
=> dfs의 경우 4방향 이동을 중복순열처럼 탐색하게 된다. (브루트포스)
=> move 함수의 경우: (1)’.’ 계속 이동, (2)’O’ 체크, (3)’R’,’B’ 겹치는지 순서로 진행!
핵심 구현 사항
- BFS를 통한 최단 이동 횟수 탐색 or DFS를 통한 전체 탐색
- 10번의 기울임까지 확인하므로 복잡도는 4^10*X 로 볼 수 있다.
- X의 경우 구슬이 움직이는 칸으로 본다면, N*M=10*10=100 으로 크지 않다.
- 따라서 DFS를 통해 전체 탐색을 해도 문제 없는 것이다. 범위를 작게 잡았으니까!
- 구슬 이동 우선순위 처리
- Red, Blue 구슬이 겹쳐지면 다시 “백” 하기 위해
- 10회 초과 시 -1 반환
- 파란 구슬이 구멍에 빠지면 실패
구슬 이동 처리
private static Pair move(int dir, Pair data) {
Pair cur = new Pair(data.rx, data.ry, data.bx, data.by);
int chk = checkPriority(cur, dir); // 이동 우선순위 결정
//(1)'.' 계속 이동
// ...
//(2)'O' 체크
// ...
//(3)'R','B' 겹치는지
// ...
return cur;
}
우선순위 체크
public static int checkPriority(Pair p, int dir) {
if(dir==0) { // 오른쪽으로 기울이기
if(p.by<p.ry) return 0; // 빨간 구슬 먼저
else return 1; // 파란 구슬 먼저
}
// ... 다른 방향들
}
전체 코드 - BFS, DFS 둘 다
BFS 방식
public class 구슬탈출2_13460_bfs {
static int N,M;
static char[][] board;
static int[] dx = {0,1,0,-1};
static int[] dy = {1,0,-1,0};
static Pair initRB;
static int result=Integer.MAX_VALUE;
static void bfs() {
Queue<Pair> qu = new ArrayDeque<>();
qu.offer(initRB);
int cnt=0;
while (!qu.isEmpty()) {
int size = qu.size();
cnt++;
for (int s = 0; s < size; s++) {
Pair cur = qu.poll();
for (int i = 0; i < 4; i++) {
Pair nxt = move(i, cur);
if(nxt.priority==2) continue;
if (nxt.priority==1) {
result = cnt;
return; //bfs라 어차피 최소값임. 바로 탈출하여 게임 종료하자.
}
qu.offer(nxt);
}
}
if (cnt >= 10) { // 10회 초과부턴 무조건 -1임
result=-1;
break;
}
}
}
public static int checkPriority(Pair p, int dir) {
if(dir==0) { // '->'
if(p.by<p.ry) return 0; // 0:R
else return 1;
}
if(dir==1) { // '아래'
if(p.bx<p.rx) return 0;
else return 1;
}
if(dir==2) { // '<-'
if(p.by<p.ry) return 1; // 1:B
else return 0;
}
if(dir==3) { // '위'
if(p.bx<p.rx) return 1;
else return 0;
}
return -1;
}
private static Pair move(int dir, Pair data) {
//(1) '.' 끝까지 이동
Pair cur = new Pair(data.rx, data.ry, data.bx, data.by);
int chk = checkPriority(cur, dir); // 0:R, 1:B
while (true) {
//base condition
if(board[cur.rx+dx[dir]][cur.ry+dy[dir]] != '.' && board[cur.bx+dx[dir]][cur.by+dy[dir]] != '.') break;
if (board[cur.rx+dx[dir]][cur.ry+dy[dir]] == '.') {
cur.rx+=dx[dir];
cur.ry+=dy[dir];
}
if (board[cur.bx+dx[dir]][cur.by+dy[dir]] == '.') {
cur.bx+=dx[dir];
cur.by+=dy[dir];
}
}
//(2) 'O' 인지 체크
if (board[cur.rx+dx[dir]][cur.ry+dy[dir]] == 'O') {
cur.priority = 1; //red
}
if (board[cur.bx+dx[dir]][cur.by+dy[dir]] == 'O') {
cur.priority = 2; //blue
}
//(3) R,B 겹치면 해결
if (cur.rx == cur.bx && cur.ry == cur.by) {
if(chk==0) { // 'R'이 우선이라 'B'가 양보
cur.bx -= dx[dir];
cur.by -= dy[dir];
}else if(chk==1) { // 'B'가 우선이라 'R'이 양보
cur.rx -= dx[dir];
cur.ry -= dy[dir];
}
}
return cur;
}
public static void main(String[] args) throws IOException {
//input
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer stk = new StringTokenizer(br.readLine(), " ");
N = Integer.parseInt(stk.nextToken());
M = Integer.parseInt(stk.nextToken());
board = new char[N + 1][M + 1];
initRB = new Pair(0,0,0,0);
for (int i = 0; i < N; i++) {
String input=br.readLine();
for (int j = 0; j < M; j++) {
board[i][j]=input.charAt(j);
if (board[i][j] == 'B') {
initRB.bx=i; initRB.by=j;
board[i][j]='.'; //어차피 R,B 겹치는건 따로 처리할거라 빈공간('.')로 표시
}else if (board[i][j] == 'R') {
initRB.rx=i; initRB.ry=j;
board[i][j]='.';
}
}
}
//run
bfs();
//output
System.out.println(result);
}
static class Pair {
int rx; int ry; int bx; int by; int priority=0;
public Pair(int rx, int ry, int bx, int by) {
this.rx=rx; this.ry=ry; this.bx=bx; this.by=by;
}
@Override
public String toString() {
return "Pair{" +
"rx=" + rx +
", ry=" + ry +
", bx=" + bx +
", by=" + by +
", priority=" + priority +
'}';
}
}
}
DFS 방식
public class 구슬탈출2_13460_dfs {
static int N,M;
static char[][] board;
static int[] dx = {0,1,0,-1};
static int[] dy = {1,0,-1,0};
static Pair initRB;
static int result=Integer.MAX_VALUE;
//중복순열과 유사
static void dfs(int depth, Pair cur) {
//base condition
if (depth == 10 || result < depth) {
return; // 10회까지 가능, 깊이가 result 보다 큰건 최소가 아니라서 필요가 없음
}
//recursion
for (int i = 0; i < 4; i++) {
Pair nxt = move(i, cur);
if(nxt.priority==2) continue;
if (nxt.priority == 1) {
result = Math.min(result,depth+1);
return;
}
dfs(depth + 1, nxt);
}
}
public static int checkPriority(Pair p, int dir) {
if(dir==0) { // '->'
if(p.by<p.ry) return 0; // 0:R
else return 1;
}
if(dir==1) { // '아래'
if(p.bx<p.rx) return 0;
else return 1;
}
if(dir==2) { // '<-'
if(p.by<p.ry) return 1; // 1:B
else return 0;
}
if(dir==3) { // '위'
if(p.bx<p.rx) return 1;
else return 0;
}
return -1;
}
private static Pair move(int dir, Pair data) {
//(1) '.' 끝까지 이동
Pair cur = new Pair(data.rx, data.ry, data.bx, data.by);
int chk = checkPriority(cur, dir); // 0:R, 1:B
while (true) {
//base condition
if(board[cur.rx+dx[dir]][cur.ry+dy[dir]] != '.' && board[cur.bx+dx[dir]][cur.by+dy[dir]] != '.') break;
if (board[cur.rx+dx[dir]][cur.ry+dy[dir]] == '.') {
cur.rx+=dx[dir];
cur.ry+=dy[dir];
}
if (board[cur.bx+dx[dir]][cur.by+dy[dir]] == '.') {
cur.bx+=dx[dir];
cur.by+=dy[dir];
}
}
//(2) 'O' 인지 체크
if (board[cur.rx+dx[dir]][cur.ry+dy[dir]] == 'O') {
cur.priority = 1; //red
}
if (board[cur.bx+dx[dir]][cur.by+dy[dir]] == 'O') {
cur.priority = 2; //blue
}
//(3) R,B 겹치면 해결
if (cur.rx == cur.bx && cur.ry == cur.by) {
if(chk==0) { // 'R'이 우선이라 'B'가 양보
cur.bx -= dx[dir];
cur.by -= dy[dir];
}else if(chk==1) { // 'B'가 우선이라 'R'이 양보
cur.rx -= dx[dir];
cur.ry -= dy[dir];
}
}
return cur;
}
public static void main(String[] args) throws IOException {
//input
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer stk = new StringTokenizer(br.readLine(), " ");
N = Integer.parseInt(stk.nextToken());
M = Integer.parseInt(stk.nextToken());
board = new char[N + 1][M + 1];
initRB = new Pair(0,0,0,0);
for (int i = 0; i < N; i++) {
String input=br.readLine();
for (int j = 0; j < M; j++) {
board[i][j]=input.charAt(j);
if (board[i][j] == 'B') {
initRB.bx=i; initRB.by=j;
board[i][j]='.'; //어차피 R,B 겹치는건 따로 처리할거라 빈공간('.')로 표시
}else if (board[i][j] == 'R') {
initRB.rx=i; initRB.ry=j;
board[i][j]='.';
}
}
}
//run
dfs(0, initRB);
//output
if(result == Integer.MAX_VALUE) result=-1;
System.out.println(result);
}
static class Pair {
int rx; int ry; int bx; int by; int priority=0;
public Pair(int rx, int ry, int bx, int by) {
this.rx=rx; this.ry=ry; this.bx=bx; this.by=by;
}
@Override
public String toString() {
return "Pair{" +
"rx=" + rx +
", ry=" + ry +
", bx=" + bx +
", by=" + by +
", priority=" + priority +
'}';
}
}
}
주관적인 생각의 풀이이므로 틀린 부분 지적은 언제나 환영합니다⚡
댓글남기기