
내가 생각했을때 문제에서 원하는부분
첫 번째 줄에 창고의 상태를 나타내는 10개의 문자가 주어진다.
각 문자는 ‘.’, ‘@’, ‘#’, ‘!’ 중 하나로, 각각 빈칸, 로봇이 있는 칸, 박스가 있는 칸, 박스를 놓아야 하는 칸을 뜻한다.
‘@’, ‘#’, ‘!’는 정확히 하나씩 주어진다.
즉, 박스가 처음부터 목표 지점에 있는 경우는 주어지지 않는다.
적어도 몇 번의 명령을 해야 박스를 원하는 칸에 둘 수 있는지 출력한다.
몇 번의 명령을 내려도 박스를 원하는 칸으로 옮길 수 없다면 대신 ‘-1’을 출력한다.
내가 이 문제를 보고 생각해본 부분
main 메서드 초기 설정:
BufferedReader를 사용하여 창고 상태를 나타내는 문자열을 한 줄 읽어온다.
robotStartPos, boxStartPos, goalPos 변수를 초기화한다.
이 변수들은 입력 문자열을 순회하면서 각각 @, #, !의 인덱스를 저장하게 된다.
for 루프를 통해 입력 문자열을 한 칸씩 확인하여 로봇, 박스, 목표 지점의 초기 위치를 찾아 저장한다.
BFS 준비:
Queue<State> queue = new LinkedList<>();: BFS 탐색을 위한 큐를 생성한다.
여기에 탐색할 State 객체들을 넣고 빼면서 탐색을 진행한다.
boolean[][] visited = new boolean[10][10];: visited 배열은 이미 탐색한 로봇(robotPos)과 박스(boxPos)의 조합을 기억하는 데 사용된다.
10x10 배열인 이유는 창고 칸이 10개이기 때문이다.
visited[r][b]가 true이면 로봇이 r에 있고 박스가 b에 있는 상태는 이미 탐색했다는 의미이다.
이렇게 중복 탐색을 막아서 무한 루프를 방지하고 효율을 높인다.
queue.offer(new State(robotStartPos, boxStartPos, 0));: 초기 로봇과 박스의 위치, 그리고 0번 이동으로 시작하는 State 객체를 큐에 넣는다.
visited[robotStartPos][boxStartPos] = true;: 초기 상태는 방문했다고 표시한다.
minMoves = -1;: 아직 목표를 찾지 못했으므로 최소 이동 횟수를 -1로 초기화한다.
BFS 루프(while (!queue.isEmpty())):
큐가 비어있지 않은 동안 계속 반복하며 탐색을 진행한다.
State current = queue.poll();: 큐의 가장 앞에 있는 State 객체(현재 상태)를 가져와 current 변수에 저장하고 큐에서 제거한다.
목표 달성 확인:
if(current.boxPos == goalPos): 현재 박스의 위치(current.boxPos)가 목표 지점(goalPos)과 같다면, 박스가 목표에 도착한 것이다.
minMoves = current.moves;: 이때 current.moves가 바로 최소 이동 횟수이므로 minMoves에 저장한다.
break;: 최단 경로를 찾았으므로 더 이상 탐색할 필요가 없어 루프를 종료한다.
로봇 이동(두 가지 경우):
로봇만 이동하는 경우(빈칸 이동):
로봇은 현재 위치(current.robotPos)에서 왼쪽(-1) 또는 오른쪽(+1)으로 이동할 수 있다.
current.robotPos - 1 >= 0: 로봇이 왼쪽으로 이동했을 때 창고 범위를 벗어나지 않는지 확인한다.
current.robotPos - 1 != current.boxPos: 이동하려는 칸이 박스가 있는 칸이 아닌지 확인한다 (박스를 밀지 않고 그냥 이동하는 경우이므로).
if(!visited[nextRobotPos][current.boxPos]): 이동하려는 로봇 위치와 현재 박스 위치의 조합이 이전에 방문한 적이 없는 상태인지 확인한다.
모든 조건을 만족하면 visited 배열을 true로 표시하고, 새로운 State 객체(nextRobotPos, current.boxPos, current.moves + 1)를 만들어 큐에 추가한다.
current.moves + 1은 한 번 이동했으므로 횟수가 1 증가한 것을 의미한다.
오른쪽 이동(current.robotPos + 1)도 동일한 방식으로 처리된다.
로봇이 박스를 미는 경우:
로봇이 박스 왼쪽에 있는 경우 (current.robotPos + 1 == current.boxPos): 로봇은 박스의 바로 왼쪽에 있다.
nextBoxPos = current.boxPos + 1: 박스를 오른쪽으로 밀었을 때 박스가 이동할 다음 위치이다.
if (nextBoxPos < 10): 박스가 창고 밖으로 밀려나지 않는지 확인한다.
nextRobotPos = current.boxPos: 로봇은 박스가 있던 자리로 이동한다.
방문 여부(!visited[nextRobotPos][nextBoxPos])를 확인하고, 방문하지 않았다면 visited를 true로 표시한 후 새로운 State를 큐에 추가한다.
로봇이 박스 오른쪽에 있는 경우 (current.robotPos - 1 == current.boxPos): 로봇은 박스의 바로 오른쪽에 있다.
nextBoxPos = current.boxPos - 1: 박스를 왼쪽으로 밀었을 때 박스가 이동할 다음 위치이다.
if (nextBoxPos >= 0): 박스가 창고 밖으로 밀려나지 않는지 확인한다.
nextRobotPos = current.boxPos: 로봇은 박스가 있던 자리로 이동한다.
동일하게 방문 여부를 확인하고 큐에 새로운 State를 추가한다.
결과 출력:
BFS 루프가 모두 끝난 후 (while 문 종료), minMoves 값을 출력한다.
만약 목표에 도달하지 못했다면 minMoves는 여전히 -1일 것이다.
코드로 구현
package baekjoon.baekjoon_31;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.LinkedList;
import java.util.Queue;
// 백준 31844번 문제
public class Main1246 {
// 현재 상태를 나타내는 클래스
static class State {
int robotPos; // 로봇의 위치
int boxPos; // 박스의 위치
int moves; // 현재까지 이동한 횟수
public State(int robotPos, int boxPos, int moves) {
this.robotPos = robotPos;
this.boxPos = boxPos;
this.moves = moves;
}
}
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String warehouse = br.readLine();
int robotStartPos = -1;
int boxStartPos = -1;
int goalPos = -1;
// 초기 위치 파싱
for (int i = 0; i < 10; i++) {
char c = warehouse.charAt(i);
if (c == '@') {
robotStartPos = i;
} else if (c == '#') {
boxStartPos = i;
} else if (c == '!') {
goalPos = i;
}
}
// BFS 초기화
Queue<State> queue = new LinkedList<>();
// [로봇 위치][박스 위치] = 방문 여부
boolean[][] visited = new boolean[10][10];
// 시작 상태를 큐에 추가하고 방문 처리
queue.offer(new State(robotStartPos, boxStartPos, 0));
visited[robotStartPos][boxStartPos] = true;
int minMoves = -1; // 최소 이동 횟수, 찾지 못하면 -1
// BFS 시작
while (!queue.isEmpty()) {
State current = queue.poll();
// 박스가 목표 지점에 도달했는지 확인
if (current.boxPos == goalPos) {
minMoves = current.moves;
break; // 최단 경로를 찾았으므로 탐색 종료
}
// 1. 로봇만 이동하는 경우 (인접 빈칸)
// 왼쪽으로 이동 시도
if (current.robotPos - 1 >= 0 && current.robotPos - 1 != current.boxPos) {
int nextRobotPos = current.robotPos - 1;
if (!visited[nextRobotPos][current.boxPos]) {
visited[nextRobotPos][current.boxPos] = true;
queue.offer(new State(nextRobotPos, current.boxPos, current.moves + 1));
}
}
// 오른쪽으로 이동 시도
if (current.robotPos + 1 < 10 && current.robotPos + 1 != current.boxPos) {
int nextRobotPos = current.robotPos + 1;
if (!visited[nextRobotPos][current.boxPos]) {
visited[nextRobotPos][current.boxPos] = true;
queue.offer(new State(nextRobotPos, current.boxPos, current.moves + 1));
}
}
// 2. 로봇이 박스를 미는 경우
// 로봇이 박스 왼쪽에 있고, 오른쪽으로 박스를 미는 경우
if (current.robotPos + 1 == current.boxPos) {
int nextBoxPos = current.boxPos + 1; // 박스가 밀려날 위치
if (nextBoxPos < 10) { // 박스가 창고 밖으로 밀려나지 않는다면
int nextRobotPos = current.boxPos; // 로봇은 박스 원래 위치로 이동
if (!visited[nextRobotPos][nextBoxPos]) {
visited[nextRobotPos][nextBoxPos] = true;
queue.offer(new State(nextRobotPos, nextBoxPos, current.moves + 1));
}
}
}
// 로봇이 박스 오른쪽에 있고, 왼쪽으로 박스를 미는 경우
else if (current.robotPos - 1 == current.boxPos) {
int nextBoxPos = current.boxPos - 1; // 박스가 밀려날 위치
if (nextBoxPos >= 0) { // 박스가 창고 밖으로 밀려나지 않는다면
int nextRobotPos = current.boxPos; // 로봇은 박스 원래 위치로 이동
if (!visited[nextRobotPos][nextBoxPos]) {
visited[nextRobotPos][nextBoxPos] = true;
queue.offer(new State(nextRobotPos, nextBoxPos, current.moves + 1));
}
}
}
}
System.out.println(minMoves);
br.close();
}
}
코드와 설명이 부족할수 있습니다. 코드를 보시고 문제가 있거나 코드 개선이 필요한 부분이 있다면 댓글로 말해주시면 감사한 마음으로 참고해 코드를 수정 하겠습니다.