일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
- 프론트엔드
- 시뮬레이션
- REACT
- 오블완
- 구현
- 티스토리챌린지
- 기본 문법
- 자료 구조
- poiemaweb
- 자바스크립트
- 개발 회고
- js
- 코딩일기
- 백준
- Next.js
- State
- HTML5
- 토이 프로젝트
- three.js
- 세그먼트 트리
- 엔트리포인트
- 브루트포스
- 자바
- styled-components
- 수학
- 회고
- 모던 자바스크립트 튜토리얼
- 해시를 사용한 집합과 맵
- JavaScript
- react-three/fiber
- Today
- Total
코딩하는 고릴라
[JAVA] BOJ_19236. 청소년 상어 본문
🐒 문제
19236번: 청소년 상어
첫째 줄부터 4개의 줄에 각 칸의 들어있는 물고기의 정보가 1번 행부터 순서대로 주어진다. 물고기의 정보는 두 정수 ai, bi로 이루어져 있고, ai는 물고기의 번호, bi는 방향을 의미한다. 방향 bi는
www.acmicpc.net

🐈 풀이
1. 어떤 결과값을 도출해야 할까?
상어가 움직일 수 있는 모든 경우의 수를 고려하여, 먹을 수 있는 물고기 번호의 합의 최댓값을 구해야 한다.모든 경우의 수를 고려함을 보아 백트래킹 알고리즘을 통한 접근이 필요할 수 있음을 알 수 있다.
2. 어떻게 도출해낼까?
주어진 문제의 흐름을 따라가보면, [물고기의 이동 -> 상어의 이동] 이 계속해서 반복한다.
따라서, 우리는 물고기의 이동과 상어의 이동을 구현해야한다.
위 사항들을 구현하기 위해 3차원 배열과 HashMap을 도구로 사용했다.
- 3차원 배열 : 물고기의 번호, 방향 저장
- HashMap : 물고기의 번호를 key로, 물고기의 좌표를 저장
물고기, 상어의 이동을 구현할 때 위 두가지 도구들을 갱신해주면서 풀이해 나갔다.
1) 물고기의 이동
- 물고기가 바라보고 있는 방향으로 한 칸 이동한다.
- 이동 방식은 이동할 위치에 있는 물고기와 자리를 바꾸는 방식이다.
- 이동할 위치에 물고기가 없다면, 현재 물고기만 이동시켜주면 된다.
=> 3차원 배열의 자리바꿈, HashMap value값 변경을 통해 구현
- 물고기가 바라보고 있는 방향이 배열의 끝이거나 상어라면?
- 반시계 방향으로 45도 방향을 바꾼다.
=> 8방향 델타배열을 통해 구현
2) 상어의 이동
- 상어가 바라보고 있는 방향에 물고기가 있다면, 해당 위치로 이동한다.
- 물고기의 움직임과는 차이가 있는데, 물고기는 단 한 칸만 이동할 수 있다면, 상어는 맵의 끝까지 이동할 수 있다.

- 위 사진처럼 상어가 [0, 0]에 위치하고 있고 우하단을 바라보고 있다면, 1칸 ~ 3칸의 범위 내에서 이동할 수 있다.
- 상어가 바라보고 있는 방향에 물고기가 없다면, 더 이상 이동하지 못한다.
=> 백트래킹과 동시에 3차원 배열, HashMap의 갱신을 통해 구현
3. 특별히 고려해야 할 사항은?
1) 상어는 1칸 ~ 3칸의 범위 내에서 이동할 수 있다.
- 이 부분을 백트래킹을 통해 구현해줘야 한다. 1칸을 이동한 경우에서부터 끝까지,, 2칸을 이동한 경우에서부터 끝까지,,
2) 상어가 이동하는 모든 경우를 고려하기 위해, 물고기와 상어의 상태를 저장해 둔 3차원 배열은 어떻게 처리해줘야하나?
- 상어의 이동이 발생할 때 마다 기존의 3차원 배열을 복사해서 사용한다. 이렇게 되면 다른 경우의 수를 고려할 때 3차원 배열이 망가져있는 경우가 발생하지 않는다.
🐕🦺 소스 코드
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.StringTokenizer;
public class Main {
static int[] dr = { -1, -1, 0, 1, 1, 1, 0, -1 };
static int[] dc = { 0, -1, -1, -1, 0, 1, 1, 1 };
static int max;
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st;
StringBuilder sb = new StringBuilder();
// map[r][c][0] = 물고기 번호
// map[r][c][1] = 물고기 방향
int[][][] map = new int[4][4][2];
// HashMap<물고기번호, 물고기좌표>
HashMap<Integer, int[]> pos = new HashMap<>();
// 물고기 정보 입력받기
for (int i = 0; i < 4; i++) {
st = new StringTokenizer(br.readLine());
for (int j = 0; j < 4; j++) {
int num = Integer.parseInt(st.nextToken(" "));
// 델타배열을 0번부터 시작하게끔 만들어놓았기 때문에,
// 방향을 나타내는 숫자는 1을 빼서 저장한다.
int dir = Integer.parseInt(st.nextToken(" ")) - 1;
// 배열에 물고기 번호, 방향 저장
map[i][j] = new int[] { num, dir };
// HashMap에 (물고기 번호 : 물고기 좌표) 저장
pos.put(num, new int[] { i, j });
}
}
// map[i][j][0] = -1 : 상어
// map[i][j][0] = 0 : 빈공간
//// 상어 초기 설정
// 첫 위치에 있는 물고기 제거
pos.remove(map[0][0][0]);
// 상어의 위치 저장
pos.put(-1, new int[] { 0, 0 });
// 초기 점수
int initScore = map[0][0][0];
// 해당 위치를 상어로 만든다
map[0][0][0] = -1;
// 물고기 이동
fishMove(map, pos, initScore);
System.out.println(max);
}
static void fishMove(int[][][] map, HashMap<Integer, int[]> pos, int score) {
// 물고기 자리 이동
for (int i = 1; i <= 16; i++) {
// 해당 번호의 물고기가 없다면(이미 잡아먹혔다면) 넘기기
if (pos.get(i) == null) {
continue;
}
// 물고기의 위치 r, c
int r = pos.get(i)[0];
int c = pos.get(i)[1];
// 받아온 물고기가 상어나 빈 공간이 아니라면
if (map[r][c][0] != 0 && map[r][c][0] != -1) {
// 현재 물고기가 이동 가능한 경로를 8방 탐색
for (int k = 0; k < 8; k++) {
int nr = r + dr[map[r][c][1]];
int nc = c + dc[map[r][c][1]];
// 다음 좌표가 배열을 벗어나지 않고, 상어가 아닐 때
if (nr >= 0 && nc >= 0 && nr < 4 && nc < 4 && map[nr][nc][0] != -1) {
//// 1. 자리 바꾸기 (HashMap 갱신)
// 현재 물고기의 자리를 새롭게 갱신
pos.put(map[r][c][0], new int[] { nr, nc });
// 물고기의 다음 이동장소가 빈 공간이 아닐때만
if (map[nr][nc][0] != 0) {
// 해당 물고기의 위치도 바꿔준다
pos.put(map[nr][nc][0], new int[] { r, c });
}
//// 2. 자리 바꾸기 (배열 갱신)
int[] tmp = map[r][c];
map[r][c] = map[nr][nc];
map[nr][nc] = tmp;
break;
} else {
// 물고기 방향 바꾸기
map[r][c][1] = (map[r][c][1] + 1) % 8;
}
}
}
}
sharkMove(map, pos, score);
}
static void sharkMove(int[][][] map, HashMap<Integer, int[]> cpos, int score) {
// 점수 갱신
if (score > max) {
max = score;
}
// 상어의 위치 r, c
int[] shark = cpos.get(-1);
int r = shark[0];
int c = shark[1];
// 상어는 현재 맵에서 최대 3칸까지 이동 가능. 이동가능한 다음 좌표 탐색
for (int i = 1; i <= 3; i++) {
int nr = r + dr[map[r][c][1]] * i;
int nc = c + dc[map[r][c][1]] * i;
// 다음 이동위치가 배열을 벗어나지 않고, 빈 공간이 아니라면
if (nr >= 0 && nc >= 0 && nr < 4 && nc < 4 && map[nr][nc][0] != 0) {
//// 백트래킹을 위한 준비과정 - 현재의 상태를 기억해둬야 한다.
// 물고기별 좌표를 담고있는 HashMap 복사
HashMap<Integer, int[]> pos = (HashMap<Integer, int[]>) cpos.clone();
// 현재 HashMap 복사
int[][][] copyMap = new int[4][4][2];
for (int k = 0; k < 4; k++) {
for (int j = 0; j < 4; j++) {
copyMap[k][j] = map[k][j].clone();
}
}
// 이동할 좌표에 있는 물고기의 번호(점수)
int fish = copyMap[nr][nc][0];
//// 상어 이동시키기
// 1. HashMap 갱신
pos.remove(copyMap[nr][nc][0]);
pos.put(-1, new int[] { nr, nc });
// 2. 배열 갱신
copyMap[nr][nc][0] = -1;
copyMap[r][c][0] = 0;
// 물고기 이동시키기
fishMove(copyMap, pos, score + fish);
}
}
}
}
🐖 Think
- 상어의 이동이 발생할 때 마다 배열과 더불어 해시맵까지 복사해서 사용하였는데, 이를 너무 남발한 기분이 없지않아 있다. 더 깔끔한 방법이 있었을까?
'APS' 카테고리의 다른 글
[JAVA] BOJ_2910. 빈도 정렬 (0) | 2023.11.04 |
---|---|
[JAVA] BOJ_1269. 대칭 차집합 (0) | 2023.10.26 |
[JAVA] BOJ_7626. 직사각형 (4) | 2023.10.18 |
[JAVA] BOJ_3055. 탈출 (0) | 2023.10.17 |
[JAVA] BOJ_1654. 랜선 자르기 (1) | 2023.10.10 |