Algorithm/Problem
마법사 상어와 토네이도(BJ_G3_20057)
당진개발자
2024. 2. 25. 14:43
1. 문제 링크
https://www.acmicpc.net/problem/20057
20057번: 마법사 상어와 토네이도
마법사 상어가 토네이도를 배웠고, 오늘은 토네이도를 크기가 N×N인 격자로 나누어진 모래밭에서 연습하려고 한다. 위치 (r, c)는 격자의 r행 c열을 의미하고, A[r][c]는 (r, c)에 있는 모래의 양을
www.acmicpc.net
2. 나의 코드
메모리: 84288kb
시간: 588ms
코드 길이: 4315B
시간 복잡도 : O(N^2)
설명
- 상하좌우 이동 시 확인해야 하는 곳을 따로 함수로 구현
- 맵 밖으로 나가면 result에 더하면서 달팽이 모양으로 탐색
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.StringTokenizer;
public class BJ_20057 {
// 좌 하 우 상
static int[] dr = {0, 1, 0, -1};
static int[] dc = {-1, 0, 1, 0};
static int N, nowR, nowC, result;
static int[][] map;
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = null;
N = Integer.parseInt(br.readLine());
map = new int[N][N];
for (int i = 0; i< N; i++) {
st = new StringTokenizer(br.readLine());
for (int j = 0; j < N; j++) {
map[i][j] = Integer.parseInt(st.nextToken());
}
}
nowR = N / 2; nowC = N / 2;
solve();
System.out.println(result);
}
private static void solve() {
int d = 0; // 이동 방향
int s = 1; // 이동 거리
while (true) {
for (int i = 0; i < 2; i++) {
for (int j = 0; j < s; j++) {
if (nowR == 0 && nowC == 0) return; // 만약 시작 지점으로 돌아 왔으면 끝내기
int nr = nowR + dr[d];
int nc = nowC + dc[d];
switch (d) {
case 0:
left(nr, nc, d);
break;
case 1:
down(nr, nc, d);
break;
case 2:
right(nr, nc, d);
break;
case 3:
up(nr, nc, d);
break;
}
nowR = nr;
nowC = nc;
}
d = (d + 1) % 4; // 방향 바꾸기
}
++s; // 이동 거리 늘려주기
}
}
private static boolean isValid (int r, int c) {
return 0 <= r && r < N && 0 <= c && c < N;
}
// 왼쪽으로 이동 시
private static void left(int r, int c, int d) {
int[] dx = {-2, -1, -1, -1, 0, 1, 1, 1, 2};
int[] dy = {0, -1, 0, 1, -2, -1, 0, 1, 0};
double[] percent = {2.0, 10.0, 7.0, 1.0, 5.0, 10.0, 7.0, 1.0, 2.0};
check(r, c, d, dx, dy, percent);
}
// 아래쪽으로 이동 시
private static void down(int r, int c, int d) {
int[] dx = {-1, -1, 0, 0, 0, 0, 1, 1, 2};
int[] dy = {-1, 1, -2, -1, 1, 2, -1, 1, 0};
double[] percent = {1, 1, 2, 7, 7, 2, 10, 10, 5};
check(r, c, d, dx, dy, percent);
}
// 오른쪽으로 이동 시
private static void right(int r, int c, int d) {
int[] dx = {-2, -1, -1, -1, 0, 1, 1, 1, 2};
int[] dy = {0, -1, 0, 1, 2, -1, 0, 1, 0};
double[] percent = {2, 1, 7, 10, 5, 1, 7, 10, 2};
check(r, c, d, dx, dy, percent);
}
// 위쪽으로 이동 시
private static void up(int r, int c, int d) {
int[] dx = {-2, -1, -1, 0, 0, 0, 0, 1, 1};
int[] dy = {0, -1, 1, -2, -1, 1, 2, -1, 1};
double[] percent = {5, 10, 10, 2, 7, 7, 2, 1, 1};
check(r, c, d, dx, dy, percent);
}
private static void check(int r, int c, int d, int[] dx, int[] dy, double[] percent) {
int moveSand = 0;
for (int i = 0; i < 9; i++) {
int nr = r + dx[i];
int nc = c + dy[i];
int value = (int) Math.floor(map[r][c] * percent[i] / 100.0); // 이동 할 모래의 값
if (isValid(nr, nc)) {
map[nr][nc] += value;
moveSand += value;
} else {
result += value;
moveSand += value;
}
}
int nr = r + dr[d];
int nc = c + dc[d];
// 만약 격자 밖으로 나가지 않았다면
if (isValid(nr, nc)) {
map[nr][nc] += map[r][c] - moveSand;
} else {
result += map[r][c] - moveSand;
}
map[r][c] = 0;
}
}
3. 정답 코드
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
public class Main {
static int N, answer;
// 9시 방향부터 반시계 방향
static int[] dr = {0, 1, 1, 1, 0, -1, -1, -1};
static int[] dc = {-1, -1, 0, 1, 1, 1, 0, -1};
static int[][] map;
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringBuilder sb = new StringBuilder();
StringTokenizer st;
N = Integer.parseInt(br.readLine());
map = new int[N][N];
for (int i = 0; i < N; i++) {
st = new StringTokenizer(br.readLine());
for (int j = 0; j < N; j++) {
map[i][j] = Integer.parseInt(st.nextToken());
}
}
play();
System.out.println(answer);
}
// 토네이도 시전
private static void play() {
// 토네이도의 위치와 방향
int r = N / 2;
int c = N / 2;
int dir = 0;
int s;
for (s = 1; s < N; s++) {
// 이동해야하는 칸수만큼 반복
for (int i = 0; i < s * 2; i++) {
// 방향 전환
if (i == s) {
dir = (dir + 2) % 8;
}
r += dr[dir];
c += dc[dir];
blowSand(r, c, dir);
}
// 방향 전환
dir = (dir + 2) % 8;
}
for (int i = 0; i < s - 1; i++) {
r += dr[dir];
c += dc[dir];
blowSand(r, c, dir);
}
}
// 모래를 흩날리게 하는 기능을 가진 메서드
private static void blowSand(int r, int c, int dir) {
int sand = map[r][c];
int blowedSand = 0;
int nr;
int nc;
int nDir;
for (int i = 0; i < 8; i++) {
nDir = (dir + i) % 8;
if (i == 0) {
nr = r + 2 * dr[nDir];
nc = c + 2 * dc[nDir];
if (!isIn(nr, nc)) {
answer += sand * 0.05;
} else {
map[nr][nc] += sand * 0.05;
}
blowedSand += sand * 0.05;
} else if (i == 1 || i == 7) {
nr = r + dr[nDir];
nc = c + dc[nDir];
if (!isIn(nr, nc)) {
answer += sand * 0.1;
} else {
map[nr][nc] += sand * 0.1;
}
blowedSand += sand * 0.1;
} else if (i == 2 || i == 6) {
nr = r + dr[nDir];
nc = c + dc[nDir];
if (!isIn(nr, nc)) {
answer += sand * 0.07;
} else {
map[nr][nc] += sand * 0.07;
}
blowedSand += sand * 0.07;
nr = r + 2 * dr[nDir];
nc = c + 2 * dc[nDir];
if (!isIn(nr, nc)) {
answer += sand * 0.02;
} else {
map[nr][nc] += sand * 0.02;
}
blowedSand += sand * 0.02;
} else if (i == 3 || i == 5) {
nr = r + dr[nDir];
nc = c + dc[nDir];
if (!isIn(nr, nc)) {
answer += sand * 0.01;
} else {
map[nr][nc] += sand * 0.01;
}
blowedSand += sand * 0.01;
}
}
nr = r + dr[dir];
nc = c + dc[dir];
if (!isIn(nr, nc)) {
answer += sand - blowedSand;
} else {
map[nr][nc] += sand - blowedSand;
}
map[r][c] = 0;
}
private static boolean isIn(int r, int c) {
return r >= 0 && c >= 0 && r < N && c < N;
}
}
4. 배운 점
- 구현 문제는 처음에 잘 못 만들면 디버깅이 어렵다.
- 처음에 설계를 잘하자.