이번 포스팅에서는 백준 3190번 "뱀" 문제를 DFS를 이용하여 해결했던 과정을 공유함.
뱀이 이동하면서 사과를 먹으면 몸이 늘어나고, 자신의 몸이나 벽에 부딪히면 게임이 종료되는 문제였음.
특정 초마다 주어지는 방향 전환 명령에 따라 뱀의 이동을 시뮬레이션하여 최대 이동 시간을 구하는 것이 목표였음.
문제를 해결하기 위해 DFS (재귀적 시뮬레이션)를 사용하여 매 초마다 뱀의 상태를 업데이트함.
주요 아이디어는 아래와 같음:
맵 상태 관리 🗺️
map
배열로 격자 상태를 관리함 뱀의 몸 추적 🔍
nextBody
배열을 활용함DFS 시뮬레이션 ⏱️
moveDfs
함수를 재귀 호출하여, 방향 전환 명령 처리 🔀
0
: 왼쪽 회전 1
: 오른쪽 회전아래는 주요 코드 부분에 대한 설명임:
N×N
격자, 사과(1) 위치 설정 [이동 시간(초), 방향 전환 (0: 왼쪽, 1: 오른쪽)]
를 저장private static int moveDfs(int hx, int hy, int tx, int ty, int curDir, int time) {
// ⏰ 시간에 맞춰 방향 전환 처리
if (queue.size() > 0 && queue.peek()[0] == time) {
int nextDir = queue.poll()[1];
if (nextDir == 0) curDir = (curDir - 1 + 4) % 4;
else curDir = (curDir + 1) % 4;
}
// ➡️ 새로운 머리 좌표 계산
int nhx = hx + dir[curDir][0];
int nhy = hy + dir[curDir][1];
// 🚧 벽에 부딪히면 종료
if (nhx < 0 || nhx >= N || nhy < 0 || nhy >= N) {
return time;
}
// ❌ 자기 몸에 부딪히면 종료
if (map[nhx][nhy] == 2) {
return time;
}
// 📍 현재 머리 위치에서 다음 몸의 좌표 갱신 (꼬리 이동 추적)
nextBody[hx][hy][0] = nhx;
nextBody[hx][hy][1] = nhy;
int ntx = tx;
int nty = ty;
// 🍏 사과가 없으면 꼬리 이동: 꼬리 위치를 빈칸(0)으로 만들고, 다음 꼬리 좌표 갱신
if (map[nhx][nhy] != 1) {
map[tx][ty] = 0;
ntx = nextBody[tx][ty][0];
nty = nextBody[tx][ty][1];
}
// 🐍 새로운 머리 좌표에 뱀 몸 표시
map[nhx][nhy] = 2;
// ▶️ 다음 초로 재귀 호출 (시간 1 증가)
return moveDfs(nhx, nhy, ntx, nty, curDir, time + 1);
}
nextBody
배열로 갱신하며 꼬리 위치를 업데이트함.getAnswer()
함수에서 재귀 호출을 시작하여,import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayDeque;
import java.util.Queue;
import java.util.StringTokenizer;
public class Main {
private static StringTokenizer st;
private static StringBuilder sb = new StringBuilder();
private static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
private static int N, K, L;
private static int[][] map; // 사과: 1, 뱀 몸: 2, 빈 공간: 0
private static Queue<int[]> queue = new ArrayDeque<>(); // [이동 시간(초), 방향 전환: 0(왼쪽), 1(오른쪽)]
private static int[][] dir = {{-1, 0}, {0, 1}, {1, 0}, {0, -1}};
private static int[][][] nextBody; // 각 몸통 위치의 다음 좌표 저장
public static void main(String[] args) throws IOException {
inputSetting();
map[0][0] = 2; // 시작 위치 (머리) 표시
System.out.println(getAnswer());
}
private static int getAnswer() {
// 초기 상태: (머리 좌표: 0,0), (꼬리 좌표: 0,0), 현재 방향: 1 (오른쪽), 시간: 1
return moveDfs(0, 0, 0, 0, 1, 1);
}
private static int moveDfs(int hx, int hy, int tx, int ty, int curDir, int time) {
// ⏰ 시간에 맞춰 방향 전환 처리
if (queue.size() > 0 && queue.peek()[0] == time) {
int nextDir = queue.poll()[1];
if (nextDir == 0) curDir = (curDir - 1 + 4) % 4;
else curDir = (curDir + 1) % 4;
}
// ➡️ 새로운 머리 좌표 계산
int nhx = hx + dir[curDir][0];
int nhy = hy + dir[curDir][1];
// 🚧 벽에 부딪히면 종료
if (nhx < 0 || nhx >= N || nhy < 0 || nhy >= N) {
return time;
}
// ❌ 자기 몸에 부딪히면 종료
if (map[nhx][nhy] == 2) {
return time;
}
// 📍 현재 머리 위치에서 다음 몸의 좌표 갱신 (꼬리 이동 추적)
nextBody[hx][hy][0] = nhx;
nextBody[hx][hy][1] = nhy;
int ntx = tx;
int nty = ty;
// 🍏 사과가 없으면 꼬리 이동: 꼬리 위치를 빈칸(0)으로 만들고, 다음 꼬리 좌표 갱신
if (map[nhx][nhy] != 1) {
map[tx][ty] = 0;
ntx = nextBody[tx][ty][0];
nty = nextBody[tx][ty][1];
}
// 🐍 새 머리 좌표에 뱀 몸 표시
map[nhx][nhy] = 2;
// ▶️ 다음 초로 재귀 호출 (시간 1 증가)
return moveDfs(nhx, nhy, ntx, nty, curDir, time + 1);
}
private static void inputSetting() throws IOException {
N = Integer.parseInt(br.readLine());
map = new int[N][N];
nextBody = new int[N][N][2];
K = Integer.parseInt(br.readLine());
for (int i = 0; i < K; i++) {
st = new StringTokenizer(br.readLine());
int ax = Integer.parseInt(st.nextToken()) - 1;
int ay = Integer.parseInt(st.nextToken()) - 1;
map[ax][ay] = 1;
}
L = Integer.parseInt(br.readLine());
for (int i = 0; i < L; i++) {
st = new StringTokenizer(br.readLine());
int second = Integer.parseInt(st.nextToken());
int nextMove = (st.nextToken().equals("D")) ? 1 : 0;
queue.add(new int[]{second, nextMove});
}
}
}
nextBody
배열을 통해 각 몸통의 다음 이동 좌표를 저장하여 꼬리 이동을 정확하게 추적함.