[알고리즘] 낚시왕
2024. 2. 27. 15:53ㆍ알고리즘 풀이/Java
https://www.acmicpc.net/problem/17143
17143번: 낚시왕
낚시왕이 상어 낚시를 하는 곳은 크기가 R×C인 격자판으로 나타낼 수 있다. 격자판의 각 칸은 (r, c)로 나타낼 수 있다. r은 행, c는 열이고, (R, C)는 아래 그림에서 가장 오른쪽 아래에 있는 칸이다.
www.acmicpc.net
import java.awt.Point;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
/**
* Set은 매우 느립니다. List나 배열을 사용할 수 있는 상황에서는 굳이 쓰지 맙시다!
*/
/**
* 이차원 좌표맵에서 시간마다 움직이는 상어의 움직임과 상호작용(잡아먹음)을 시뮬레이션으로 만들어야 하는 문제이다.
* 낚시꾼은 시간마다 오른쪽으로 움직이며 그 열의 제일 위의 행의 상어 한마리를 잡는다.
* 순서는 다음과 같다.
* 1. 낚시꾼이 시간마다 오른쪽으로 움직인다.
* 2. 상어를 잡는다.
* 3. 상어가 움직인다.
*
* 이에 따라 풀이 또한 위의 세 단계로 나누어 주었다.
* 제일 먼저 낚시꾼을 움직이고 상어를 잡는다. 여기까지는 간단하다.
*
* 문제는 상어의 움직임을 구현해야 하는 것이다.
* 모든 상어가 움직여야 하기 때문에 상어의 위치를 따로 자료구조로 저장한다.
* 그 후 각 위치에 있는 상어들의 좌표를 속도와 방향에 따라 갱신해준다.
* 이미 그 좌표에 상어가 있다면 크기를 비교하여 더 큰 상어만 남겨준다.
*
* 주의할 점은 상어의 속도는 1부터 1000까지라는 것이다.
* 최대 10,000마리의 상어에 대해 1000까지를 일일이 시뮬레이션을 돌리며 모두 움직여주게 되면 10,000,000이 나오고 여기에 낚시꾼은 최대 100까지 움직일 수 있기 때문에 시간 초과가 난다.
* 따라서 주기를 이용하여 상어의 움직임 시뮬레이션을 줄여준다.
*/
public class Main {
static int R; // 행
static int C; // 열
static int totalSize; // 잡은 상어 크기의 합
static int[][][] map;
static int nr; // 현재 행
static int nc; // 현재 열
static int direction; // 현재 방향
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st;
st = new StringTokenizer(br.readLine());
R = Integer.parseInt(st.nextToken());
C = Integer.parseInt(st.nextToken());
int N = Integer.parseInt(st.nextToken());
map = new int[R + 1][C + 1][];
/**
* 상어의 위치는 겹칠 수 있기 때문에 set으로 관리한다.
*/
List<Point> sharks = new ArrayList<>();
for (int i = 0; i < N; i++) {
st = new StringTokenizer(br.readLine());
int[] shark = new int[5];
for (int j = 0; j < 5; j++) {
shark[j] = Integer.parseInt(st.nextToken());
}
Point point = new Point(shark[0], shark[1]);
map[shark[0]][shark[1]] = new int[]{shark[2], shark[3], shark[4]};
sharks.add(point);
}
int fishingKingLoc = 0;
while (fishingKingLoc != C) {
// 낚시왕 이동
fishingKingLoc++;
// 상어 잡기
int size = -1;
for (int i = 1; i <= R; i++) {
if (map[i][fishingKingLoc] != null) {
// 상어를 잡았다면
size = map[i][fishingKingLoc][2];
totalSize += size;
map[i][fishingKingLoc] = null;
break;
}
}
// 상어 이동
int[] dr = {0, -1, 1, 0, 0};
int[] dc = {0, 0, 0, 1, -1};
int[][][] newMap = new int[R + 1][C + 1][];
List<Point> newSharks = new ArrayList<>();
for (Point loc : sharks) {
int r = loc.x;
int c = loc.y;
int[] shark = map[r][c];
if (shark == null) {
continue;
}
int speed = shark[0];
direction = shark[1];
int sharkSize = shark[2];
nr = r;
nc = c;
int time = 0;
int newSpeed = 0;
if (direction >= 3) { // 좌우 방향이라면
newSpeed = speed % ((C - 1) * 2); // (C-1) * 2가 하나의 주기가 된다.
} else { // 위아래 방향이라면
newSpeed = speed % ((R - 1) * 2); // 마찬가지로 (R-1) * 2가 하나의 주기가 된다.
}
/**
* 주기를 모두 고려하고 남은 나머지에 대해서는 직접 움직임 시뮬레이션을 돌려준다.
*/
while (time < newSpeed) {
time++;
movingOneStep(dr, dc);
}
if (newMap[nr][nc] != null) { // 이미 그 자리에 상어가 있다면 크기에 따라 갱신한다.
int[] sameLocShark = newMap[nr][nc];
if (sameLocShark[2] < sharkSize) {
newMap[nr][nc] = new int[]{speed, direction, sharkSize};
}
} else { // 이미 그 자리에 상어가 없다면
newMap[nr][nc] = new int[]{speed, direction, sharkSize};
newSharks.add(new Point(nr, nc));
}
}
map = newMap;
sharks = newSharks;
}
System.out.println(totalSize);
}
/**
* 방향에 따라 한칸을 움직여준다.
* 만약 더 이상 갈 수 없다면 방향을 바꾸고 한칸을 움직여준다.
* @param dr : int[], 행 움직임 배열
* @param dc : int[], 열 움직임 배열
*/
private static void movingOneStep(int[] dr, int[] dc) {
int tempNr = nr + dr[direction];
int tempNc = nc + dc[direction];
if (!(1 <= tempNr && tempNr <= R && 1 <= tempNc && tempNc <= C)) {
if (direction >= 3) {
direction = Math.abs(4 - direction) + 3;
} else {
direction = Math.abs(2 - direction) + 1;
}
tempNr = nr + dr[direction];
tempNc = nc + dc[direction];
}
nr = tempNr;
nc = tempNc;
}
/**
* 디버깅용 이차원맵 출력
*/
private static void printMap() {
for (int i = 1; i <= R; i++) {
for (int j = 1; j <= C; j++) {
if (map[i][j] == null) {
System.out.print("0");
} else {
// System.out.print(" " + map[i][j].get(0)[2]);
System.out.print("1");
}
}
System.out.println();
}
}
}
- Set은 매우 느리다. List나 배열을 사용할 수 있는 상황에서는 쓰지 말자!!!!!
'알고리즘 풀이 > Java' 카테고리의 다른 글
[알고리즘] 1로 만들기 (0) | 2024.02.27 |
---|---|
[알고리즘] 최단경로 (0) | 2024.02.27 |
[알고리즘][X] 공주님의 정원 (0) | 2024.02.26 |
[알고리즘] 점심 식사시간 (0) | 2024.02.25 |
[알고리즘] DP 테이블의 차원에 대한 꿀팁 (0) | 2024.02.24 |