[문제풀이] 새로운 게임2
업데이트:
문제 링크 : 새로운 게임2
풀이 시간 : 100분
1. 문제 풀이
백준에 올라운 삼성 SW 역량 테스트 기출 문제라고 한다.
일단 규칙에 맞게 체스 말을 이동하는 시뮬레이션과 이동 후 체스 말을 조건에 맞게 합치는 문제
블록 색깔에 따른 함수는 주석으로 설명
1) 체스 말 이동 및 합치기
1-1) 다음 위치가 범위 밖이나 파란 색일 때는 Blue() 함수
1-2) 다음 위치가 범위 안이고 하얀 색일 때는 White() 함수
1-3) 다음 위치가 범위 안이고 빨간 색일 때는 Red() 함수
2) 체스 말 이동 후 쌓여있는 체스 말의 개수 확인
2. 소스 코드
static int[] dy = { 0, 0, -1, 1 };
static int[] dx = { 1, -1, 0, 0 };
static int N;
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
N = Integer.parseInt(st.nextToken());
int K = Integer.parseInt(st.nextToken());
// 체스 말 배열
Node[] nodes = new Node[K + 1];
// map 배열 map[y][x][0]은 블록의 종류, map[y][x][1]은 맨 아래에 깔려있는 체스 말의 번호
// 입력
int[][][] map = new int[N][N][2];
for (int i = 0; i < N; i++) {
st = new StringTokenizer(br.readLine());
for (int j = 0; j < N; j++) {
map[i][j][0] = Integer.parseInt(st.nextToken());
}
}
for (int i = 1; i <= K; i++) {
st = new StringTokenizer(br.readLine());
nodes[i] = new Node(i, Integer.parseInt(st.nextToken()) - 1, Integer.parseInt(st.nextToken()) - 1,
Integer.parseInt(st.nextToken()) - 1);
map[nodes[i].y][nodes[i].x][1] = i;
}
boolean flag = true;
// 1000초를 넘어가면 -1 종료
for (int i = 1; i <= 1000; i++) {
for (int j = 1; j <= K; j++) {
// 다음 이동 위치
int tempy = nodes[j].y + dy[nodes[j].d];
int tempx = nodes[j].x + dx[nodes[j].d];
// 범위 안일 때
if (tempy >= 0 && tempy < N && tempx >= 0 && tempx < N) {
// 하얀, 빨간, 파란 색일 경우
if (map[tempy][tempx][0] == 0) {
White(map, j, nodes, tempy, tempx);
} else if (map[tempy][tempx][0] == 1) {
Red(map, j, nodes, tempy, tempx);
} else {
Blue(map, j, nodes, tempy, tempx);
}
} else {
// 범위 밖일 때는 파란색과 동일하게 작동
Blue(map, j, nodes, tempy, tempx);
}
// 현재 이동한 위치의 체스 말 개수
int count = 0;
// 현재 가장 밑에 깔려 있는 체스 말에서 시작
Node node = nodes[map[nodes[j].y][nodes[j].x][1]];
while (node != null) {
count++;
node = node.next;
if (count == 4) {
flag = false;
System.out.println(i);
break;
}
}
if (!flag) {
break;
}
}
if (!flag) {
break;
}
}
if (flag) {
System.out.println(-1);
}
}
// 파란색일 때
static void Blue(int[][][] map, int j, Node[] nodes, int tempy, int tempx) {
// 방향 바꾸기
if (nodes[j].d == 0) {
nodes[j].d = 1;
} else if (nodes[j].d == 1) {
nodes[j].d = 0;
} else if (nodes[j].d == 2) {
nodes[j].d = 3;
} else {
nodes[j].d = 2;
}
// 바꾼 방향의 위치
tempy = nodes[j].y + dy[nodes[j].d];
tempx = nodes[j].x + dx[nodes[j].d];
if (tempy >= 0 && tempy < N && tempx >= 0 && tempx < N) {
if (map[tempy][tempx][0] == 0) {
// 하얀색일 때
White(map, j, nodes, tempy, tempx);
} else if (map[tempy][tempx][0] == 1) {
// 빨간색일 때
Red(map, j, nodes, tempy, tempx);
}
}
}
// 하얀색 블록일 때
static void White(int[][][] map, int j, Node[] nodes, int tempy, int tempx) {
Node node = nodes[j];
if (node.prev != null) {
// 이동하는 체스 말이 중간일때
// 밑에 있는 체스말과 prev와 연결 끊기
node.prev.next = null;
} else {
// 맨 밑에 있을 때는 모든 체스 말과 함께 이동하기 때문에 map에 아무것도 없다고 표시
map[nodes[j].y][nodes[j].x][1] = 0;
}
node.prev = null;
if (map[tempy][tempx][1] != 0) {
// 이동 위치에 체스 말이 있을 때
// 이동 위치의 체스 말의 제일 위 블록 찾기
node = nodes[map[tempy][tempx][1]];
while (node.next != null) {
node = node.next;
}
// 제일 위에 있는 체스 말과 연결
node.next = nodes[j];
nodes[j].prev = node;
node = node.next;
} else {
// 아무것도 없을 때
map[tempy][tempx][1] = j;
}
// 이동시킨 블록들의 좌표 수정
while (node != null) {
node.y = tempy;
node.x = tempx;
node = node.next;
}
}
// 빨간 색일 경우
static void Red(int[][][] map, int j, Node[] nodes, int tempy, int tempx) {
Node node = nodes[j];
if (node.prev != null) {
// 이동하는 체스 말이 중간일때
// 밑에 있는 체스말과 prev와 연결 끊기
node.prev.next = null;
} else {
// 맨 밑에 있을 때는 모든 체스 말과 함께 이동하기 때문에 map에 아무것도 없다고 표시
map[nodes[j].y][nodes[j].x][1] = 0;
}
// 이동하는 체스 말 뒤집기
node.prev = null;
while (node.next != null) {
// 이동하는 체스 말의 위치를 이동하는 곳으로 바꾸기
node.y = tempy;
node.x = tempx;
// 연결을 뒤집어서 체스 말 뒤집기
Node temp = node.next;
node.next = node.prev;
node.prev = temp;
node = temp;
}
node.y = tempy;
node.x = tempx;
node.next = node.prev;
node.prev = null;
if (map[tempy][tempx][1] != 0) {
// 이동 위치에 체스 말이 있을 때
// 이동 위치의 체스 말의 제일 위 블록 찾기
Node temp = nodes[map[tempy][tempx][1]];
while (temp.next != null) {
temp = temp.next;
}
// 제일 위에 있는 체스 말과 연결
temp.next = node;
node.prev = temp;
} else {
// 아무것도 없을 때
map[tempy][tempx][1] = node.idx;
}
}
// 체스 말의 정보와 next, prev를 만들어 양방향 링크드리스트를 구현
static class Node {
int idx;
int y;
int x;
int d;
Node prev;
Node next;
Node(int idx, int y, int x, int d) {
this.idx = idx;
this.y = y;
this.x = x;
this.d = d;
this.prev = null;
this.next = null;
}
}
3. 후기
옛날에 시도를 했지만 못 풀었던 문제이다.
LinkedList를 직접 구현해서 next, prev를 통해서 체스의 말 이동 시 붙이고 떼는데 사용했다.
이동 후 맨 밑에 있는 체스 말을 설정하는 것이 많이 힘들었다.