Problem
매 초마다 움직이는 물고기들과 물고기를 잡아먹으며 이동하는 상어가 존재할 때
상어가 잡아 먹을 수 있는 물고기 크기의 합을 구하는 문제입니다.
Solution
백트래킹을 이용해서 상어가 이동하는 모든 경우의 수를 구하면 됩니다.
상어는 한 방향밖에 가지 못하기 때문에 4 x 4 배열에서 이동하는 경우의 수는 최대 3 개입니다.
물고기가 먼저 이동해야 하기 때문에 DFS 시작하자마자 물고기를 전부 이동시키고 이동 가능한 경우의 수를 전부 확인하여 다시 DFS 를 들어갑니다.
원래 백트래킹을 할 때는 빠져 나오면서 값들을 원래 값으로 돌려놓아야 하는데
배열이나 물고기 리스트들이 너무 바뀌는게 많기 때문에 다음 DFS 로 넘기는 값들은 전부 복사한 새로운 값으로 했습니다.
그래서 DFS 를 빠져나온 후에도 기존 값들을 갖고 다음 경우의 수를 확인할 수 있습니다.
주의할 점
- 물고기들은 번호 순서대로 이동해야 합니다. 물고기가 순서대로 주어지지 않기 때문에 정렬이 필요합니다.
- 상어는 물고기가 있는 공간으로만 이동할 수 있습니다.
- 물고기는 빈칸 또는 또다른 물고기가 있는 곳으로만 이동할 수 있습니다.
- 상어나 물고기가 이동한 후에는 이동하기 전의 공간 뒷처리를 잘 해줘야합니다.
Java Code
import java.util.*; import java.io.*; class Main { static class Shark { int x, y, dir, eatSum; Shark() { } Shark(int x, int y, int dir, int eatSum) { this.x = x; this.y = y; this.dir = dir; this.eatSum = eatSum; } } static class Fish { int x, y, id, dir; boolean isAlive = true; Fish() { } Fish(int x, int y, int id, int dir, boolean isAlive) { this.x = x; this.y = y; this.id = id; this.dir = dir; this.isAlive = isAlive; } } // 0 부터 7 까지 순서대로 ↑, ↖, ←, ↙, ↓, ↘, →, ↗ static int[] dx = {-1, -1, 0, 1, 1, 1, 0, -1}; static int[] dy = {0, -1, -1, -1, 0, 1, 1, 1}; static int maxSum = 0; public static void main(String[] args) throws Exception { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); StringTokenizer st; int[][] arr = new int[4][4]; List<Fish> fishes = new ArrayList<>(); // input for (int i = 0; i < 4; i++) { st = new StringTokenizer(br.readLine()); for (int j = 0; j < 4; j++) { Fish f = new Fish(); f.id = Integer.parseInt(st.nextToken()); f.dir = Integer.parseInt(st.nextToken()) - 1; f.x = i; f.y = j; fishes.add(f); arr[i][j] = f.id; } } // 물고기는 작은 순서부터 이동해야 하기 때문에 미리 정렬해둠 Collections.sort(fishes, new Comparator<Fish>() { @Override public int compare(Fish o1, Fish o2) { return o1.id - o2.id; } }); // 상어는 (0, 0) 물고기를 먹고 시작하며 위치는 -1 로 표현 Fish f = fishes.get(arr[0][0] - 1); Shark shark = new Shark(0, 0, f.dir, f.id); f.isAlive = false; arr[0][0] = -1; // solution dfs(arr, shark, fishes); System.out.println(maxSum); } // 재귀로 상어가 이동할 수 없을 때까지 반복한다. static void dfs(int[][] arr, Shark shark, List<Fish> fishes) { // 잡아먹은 양의 최대값 구하기 if (maxSum < shark.eatSum) { maxSum = shark.eatSum; } // 모든 물고기 순서대로 이동 fishes.forEach(e -> moveFish(e, arr, fishes)); for (int dist = 1; dist < 4; dist++) { int nx = shark.x + dx[shark.dir] * dist; int ny = shark.y + dy[shark.dir] * dist; if (0 <= nx && nx < 4 && 0 <= ny && ny < 4 && arr[nx][ny] > 0) { // 물고기 잡아먹고 dfs int[][] arrCopies = copyArr(arr); List<Fish> fishCopies = copyFishes(fishes); arrCopies[shark.x][shark.y] = 0; Fish f = fishCopies.get(arr[nx][ny] - 1); Shark newShark = new Shark(f.x, f.y, f.dir, shark.eatSum + f.id); f.isAlive = false; arrCopies[f.x][f.y] = -1; dfs(arrCopies, newShark, fishCopies); } } } // 이동가능한 칸은 빈칸, 다른 물고기가 있는 칸 // 45도 반시계 방향으로 회전하면서 스캔 static void moveFish(Fish fish, int[][] arr, List<Fish> fishes) { if (fish.isAlive == false) return; for (int i = 0; i < 8; i++) { int nextDir = (fish.dir + i) % 8; int nx = fish.x + dx[nextDir]; int ny = fish.y + dy[nextDir]; if (0 <= nx && nx < 4 && 0 <= ny && ny < 4 && arr[nx][ny] > -1) { arr[fish.x][fish.y] = 0; if (arr[nx][ny] == 0) { fish.x = nx; fish.y = ny; } else { Fish temp = fishes.get(arr[nx][ny] - 1); temp.x = fish.x; temp.y = fish.y; arr[fish.x][fish.y] = temp.id; fish.x = nx; fish.y = ny; } arr[nx][ny] = fish.id; fish.dir = nextDir; return; } } } static int[][] copyArr(int[][] arr) { int[][] temp = new int[4][4]; for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { temp[i][j] = arr[i][j]; } } return temp; } static List<Fish> copyFishes(List<Fish> fishes) { List<Fish> temp = new ArrayList<>(); fishes.forEach(e -> temp.add(new Fish(e.x, e.y, e.id, e.dir, e.isAlive))); return temp; } }
'알고리즘 문제 > Samsung' 카테고리의 다른 글
백준 17142번. 연구소 3 (Java) (0) | 2020.10.15 |
---|---|
백준 17140번. 이차원 배열과 연산 (Java) (0) | 2020.10.15 |
백준 17143번. 낚시왕 (Java) (3) | 2020.10.14 |
백준 19237번. 어른 상어 (Java) (0) | 2020.10.14 |
백준 17144번. 미세먼지 안녕! (Java) (0) | 2020.10.12 |