https://www.acmicpc.net/problem/17143
17143번: 낚시왕
낚시왕이 상어 낚시를 하는 곳은 크기가 R×C인 격자판으로 나타낼 수 있다. 격자판의 각 칸은 (r, c)로 나타낼 수 있다. r은 행, c는 열이고, (R, C)는 아래 그림에서 가장 오른쪽 아래에 있는 칸이다.
www.acmicpc.net



풀이
위 문제의 로직은 아래와 같다.
- 낚시왕이 한 칸 오른쪽으로 이동한다.
- 해당 열에 해당하는 가장 가까운 상어를 잡는다.
- 살아있는 상어들은 이동한다.
1. 낚시왕이 한 칸 오른쪽으로 이동한다.
for (int i=1; i<=C; i++) { MovePerson(i); // 이동한 열에서 가장 가까운 상어 잡기. MoveShark(); // 상어 이동. }
열의 개수만큼 반복문을 돌려서 2번, 3번 로직을 수행한다.
2. 해당 열에 해당하는 가장 가까운 상어를 잡는다.
for (int row=1; row<=R; row++) { if (map[row][col] != null) { // 상어가 있는 경우 ans += map[row][col].size; map[row][col] = null; break; } }
행의 개수만큼 반복문을 돌려서 가장 가까운 상어를 잡고, 배열에서 삭제시킨다. 위 과정까지는 큰 어려움 없이 해결할 수 있을 것이라고 생각된다.
3. 살아있는 상어들은 이동한다.
3번 로직에서 주의할 점은 두 가지가 있다.
1) 상어들이 이동하는 과정
2) 배열의 복사
public static void MoveShark() { Shark[][] copyMap = new Shark[R+1][C+1]; for (int row=1; row<=R; row++) { for (int col=1; col<=C; col++) { if (map[row][col] != null) { Shark shark = map[row][col]; int nx = shark.x; int ny = shark.y; int d = shark.dir; int speed = shark.sec; if(d == 0 || d == 1) speed %= (R - 1) * 2; else speed %= (C - 1) * 2; for (int i=0; i<speed; i++) { if (nx == 1 && d == 0) d = 1; else if (nx == R && d == 1) d = 0; else if (ny == C && d == 2) d = 3; else if (ny == 1 && d == 3) d = 2; nx += dx[d]; ny += dy[d]; } if (copyMap[nx][ny] != null && copyMap[nx][ny].size > shark.size) continue; copyMap[nx][ny] = new Shark(nx, ny, shark.sec, d, shark.size); } } } map = copyMap; }
1) 상어들이 이동하는 과정
위 문제는 상어가 맵을 벗어난 경우, 정반대의 방향으로 남은 거리만큼 이동한다. 예를 들어, (0, 2) 에 있는 상어가 왼쪽으로 5칸만큼 이동한 다고 하면 (0,1) (0,0), (0,1) (0,2)가 된다.
상어를 반복문으로 한 칸씩 이동시키면 시간 초과가 발생한다. 시간 초과 에러를 발생시키지 않으려면 점화식을 발견해야한다. 점화식은 다음과 같다.
if(d == 0 || d == 1) speed %= (R - 1) * 2; else speed %= (C - 1) * 2;
한 번에 움직이는 거리 %= (열의 크기 or 행의 크기 - 1) * 2 를 하게 되면 벽면을 찍고 제자리로 돌아온다. 여기서 발생한 나머지값만큼만 반복문을 통해서 이 동시 켜주면 된다. 위 과정을 해주는 이유는 한 마리의 상어가 양쪽의 벽을 한 번씩 찍고 제자리로 돌아오는 것은 시간을 없애주기 위함이다.
2) 배열의 복사
상어가 동시에 이동한다. 하지만 프로그램에서는 동시에 이동시킬 수 없다. 그렇다고 Map 배열을 이용해서 한 마리씩 이동시키면 "큰 상어는 작은 상어를 잡아먹는다." 조건에서 에러가 발생할 수 있다.
동시에 이동했을 경우에는 그 위치에 존재하지않지만 한 마리씩 이동했을 경우에는 그 위치에 상어가 존재할 수 있기 때문이다. 이 오류를 해결하기 위해선 copyMap을 사용해야 한다.
상어를 한 마리씩 이동시키면서 copyMap에 저장을 시킨다. copyMap에 들어간 건 이미 이동이 완료된 상어만 있기 때문에 이동한 위치에 상어가 이미 존재한다면 크기를 비교해서 잡아먹으면 된다.
// 이동을 마친 상어가 이미 존재하고, 그 상어의 크기가 더 클 경우 if (copyMap[nx][ny] != null && copyMap[nx][ny].size > shark.size) continue; copyMap[nx][ny] = new Shark(nx, ny, shark.sec, d, shark.size);
<최종코드>
import java.io.*; import java.util.*; public class Main { static int R, C, M, ans = 0; static int[] dx = {-1, 1, 0, 0}; // 위 아래 오 왼 static int[] dy = {0, 0, 1, -1}; static Shark[][] map; public static class Shark { int x, y, sec, dir, size; public Shark (int x, int y, int sec, int dir, int size) { this.x = x; this.y = y; this.sec = sec; this.dir = dir; this.size = size; } } public static void main(String[] args) throws IOException { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); StringTokenizer st = new StringTokenizer(br.readLine()); R = Integer.parseInt(st.nextToken()); // x C = Integer.parseInt(st.nextToken()); // y M = Integer.parseInt(st.nextToken()); // 상어 개수 map = new Shark[R+1][C+1]; for (int i=0; i<M; i++) { st = new StringTokenizer(br.readLine()); int x = Integer.parseInt(st.nextToken()); int y = Integer.parseInt(st.nextToken()); int s = Integer.parseInt(st.nextToken()); int d = Integer.parseInt(st.nextToken()); int z = Integer.parseInt(st.nextToken()); map[x][y] = new Shark(x, y, s, d-1, z); } // 오른쪽으로 한 칸씩 이동. for (int i=1; i<=C; i++) { MovePerson(i); // 이동한 열에서 가장 가까운 상어 잡기. MoveShark(); // 상어 이동. } System.out.println(ans); } public static void MovePerson (int col) { for (int row=1; row<=R; row++) { if (map[row][col] != null) { ans += map[row][col].size; map[row][col] = null; break; } } } public static void MoveShark() { Shark[][] copyMap = new Shark[R+1][C+1]; for (int row=1; row<=R; row++) { for (int col=1; col<=C; col++) { if (map[row][col] != null) { Shark shark = map[row][col]; int nx = shark.x; int ny = shark.y; int d = shark.dir; int speed = shark.sec; if(d == 0 || d == 1) speed %= (R - 1) * 2; else speed %= (C - 1) * 2; for (int i=0; i<speed; i++) { if (nx == 1 && d == 0) d = 1; else if (nx == R && d == 1) d = 0; else if (ny == C && d == 2) d = 3; else if (ny == 1 && d == 3) d = 2; nx += dx[d]; ny += dy[d]; } if (copyMap[nx][ny] != null && copyMap[nx][ny].size > shark.size) continue; copyMap[nx][ny] = new Shark(nx, ny, shark.sec, d, shark.size); } } } map = copyMap; } }
'알고리즘 > 구현 & 그리디 & 브루트포스' 카테고리의 다른 글
[백준]JAVA - 2564번: 경비원 (0) | 2023.01.20 |
---|---|
[백준]JAVA - 2239번: 스도쿠 (0) | 2023.01.17 |
[백준]JAVA - 1027번: 고층 건물 (1) | 2022.12.31 |
[백준]JAVA - 19236번: 청소년 상어 (1) | 2022.12.30 |
[백준]JAVA - 20058번: 마법사 상어와 파이어스톰 (1) | 2022.12.22 |