Algorithm
백준 3190번 <뱀>
seungh2
2023. 11. 21. 16:41
백준 알고리즘 3190번
https://www.acmicpc.net/problem/3190
3190번: 뱀
'Dummy' 라는 도스게임이 있다. 이 게임에는 뱀이 나와서 기어다니는데, 사과를 먹으면 뱀 길이가 늘어난다. 뱀이 이리저리 기어다니다가 벽 또는 자기자신의 몸과 부딪히면 게임이 끝난다. 게임
www.acmicpc.net
3190번
'Dummy' 라는 도스게임이 있다. 이 게임에는 뱀이 나와서 기어다니는데, 사과를 먹으면 뱀 길이가 늘어난다. 뱀이 이리저리 기어다니다가 벽 또는 자기자신의 몸과 부딪히면 게임이 끝난다.
게임은 NxN 정사각 보드위에서 진행되고, 몇몇 칸에는 사과가 놓여져 있다. 보드의 상하좌우 끝에 벽이 있다. 게임이 시작할때 뱀은 맨위 맨좌측에 위치하고 뱀의 길이는 1 이다. 뱀은 처음에 오른쪽을 향한다.
뱀은 매 초마다 이동을 하는데 다음과 같은 규칙을 따른다.
- 먼저 뱀은 몸길이를 늘려 머리를 다음칸에 위치시킨다.
- 만약 벽이나 자기자신의 몸과 부딪히면 게임이 끝난다.
- 만약 이동한 칸에 사과가 있다면, 그 칸에 있던 사과가 없어지고 꼬리는 움직이지 않는다.
- 만약 이동한 칸에 사과가 없다면, 몸길이를 줄여서 꼬리가 위치한 칸을 비워준다. 즉, 몸길이는 변하지 않는다.
사과의 위치와 뱀의 이동경로가 주어질 때 이 게임이 몇 초에 끝나는지 계산하라.
문제 해결
덱을 사용해서 문제를 풀었다.
먼저 사용한 변수는 다음과 같다.
- D : 현재 뱀의 방향(초기에는 0으로 오른쪽을 보고 있다.)
- di, dj : 뱀의 움직임. [오른쪽, 아래쪽, 왼쪽, 위쪽] 순
- apple[i][j] : (i, j) 좌표에 사과가 있으면 true
- isSnake[i][j] : (i, j) 좌표에 뱀이 있으면 true
- Q : 맨 앞에는 뱀의 꼬리가, 맨 뒤에는 뱀의 머리가 저장되어 있는 덱
- direction : key는 방향 변환하는 시간, value는 변환 방향
뱀을 움직이는 함수 move()를 만들어줬다.
- 뱀의 머리를 D 방향으로 움직인다.
- 만약 움직인 위치가 벽이거나 자기 자신을 만났다면 움직일 수 없는 것이기 때문에 false를 반환한다.
- 움직인 위치에 사과가 있다면 사과를 먹는다.
- 움직인 위치에 아무것도 없다면 뱀의 꼬리를 만다. Q에서 뱀의 꼬리를 꺼내고 isSnake의 값을 false로 업데이트한다.
- 뱀의 머리 위치를 Q에 넣고 isSnake 값을 true로 업데이트한다.
- 움직임이 가능하기 때문에 true를 반환한다.
방향을 변환하는 것은 다음과 같다.
- 뱀을 움직인다.
- Time에 +1을 한다.
- direction에서 Time의 값이 있는지 확인하고 있으면 방향을 변환한다.
코드
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.HashMap;
import java.util.Queue;
public class BOJ_3190 {
static int N, D, Time;
static int[] di = {0, 1, 0, -1};
static int[] dj = {1, 0, -1, 0};
static boolean[][] apple, isSnake;
static Deque<int[]> Q;
static HashMap<Integer, String> direction;
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
N = Integer.parseInt(br.readLine());
apple = new boolean[N][N]; // apple[i][j] : (i, j) 좌표에 사과가 있으면 true
isSnake = new boolean[N][N]; // isSnake[i][j] : (i, j) 좌표가 뱀이면 true
int M = Integer.parseInt(br.readLine()); // M : 사과의 개수
for (int i = 0; i < M; i++) {
String[] inArr = br.readLine().split(" ");
int a = Integer.parseInt(inArr[0])-1;
int b = Integer.parseInt(inArr[1])-1;
apple[a][b] = true;
}
direction = new HashMap<>(); // key : time, value : 방향.
M = Integer.parseInt(br.readLine()); // M : 방향 변환 횟수
for (int i = 0; i < M; i++) {
String[] inArr = br.readLine().split(" ");
int a = Integer.parseInt(inArr[0]);
direction.put(a, inArr[1]);
}
// end input
Q = new ArrayDeque<>(); // Q의 맨 앞 : 뱀의 꼬리, Q의 맨 뒤 : 뱀의 머리
Q.add(new int[]{0, 0});
isSnake[0][0] = true;
D = 0; // D : 뱀의 이동 방향. 처음엔 오른쪽으로
Time = 0; // Time : 절대 시간
while (true) {
// 뱀 움직이기
if (!move()) break;
Time++;
String nd = direction.getOrDefault(Time, "NO");
if (nd.equals("NO")) continue;
if (nd.equals("D")) {
D = (D + 1) % 4;
} else if (nd.equals("L")) {
D = (D - 1 + 4) % 4;
}
}
System.out.println(Time + 1);
}
static boolean move() {
if (Q.isEmpty()) return false;
int[] now = Q.peekLast();
// 새로운 머리 위치
int headI = now[0] + di[D];
int headJ = now[1] + dj[D];
// 벽을 만나면 게임 끝
if (headI >= N || headJ >= N || headI < 0 || headJ < 0) return false;
// 자기 몸을 만나면 끝
if (isSnake[headI][headJ]) return false;
if (apple[headI][headJ]) { // 사과가 있으면 사과 먹기
apple[headI][headJ] = false;
}else { // 사과가 없으면 꼬리 말기
int[] tail = Q.poll();
isSnake[tail[0]][tail[1]] = false;
}
Q.add(new int[]{headI, headJ});
isSnake[headI][headJ] = true;
return true;
}
}
결과
728x90