알고리즘
[백준] 20057 마법사 상어와 토네이도 - java
minkang
2021. 6. 27. 12:56
https://www.acmicpc.net/problem/20057
20057번: 마법사 상어와 토네이도
마법사 상어가 토네이도를 배웠고, 오늘은 토네이도를 크기가 N×N인 격자로 나누어진 모래밭에서 연습하려고 한다. 위치 (r, c)는 격자의 r행 c열을 의미하고, A[r][c]는 (r, c)에 있는 모래의 양을
www.acmicpc.net
public class 마법사상어토네이도 {
static int N;
static int map[][];
public static void main(String[] args) throws NumberFormatException, IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
N = Integer.parseInt(br.readLine());
map = new int[N][N];
StringTokenizer st;
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());
}
}
answer = 0;
solve();
System.out.println(answer);
}
private static void solve() {
int nx = N/2;
int ny = N/2;
int dir = 0;
int dirCnt = 0; // 2번 바뀔때마다 moveGoal이 1개씩 늘어남
int moveGoalCnt = 1; // 좌하 1번 우상 2번 좌하 3번씩, 이동해야하는 횟수가 1씩 늘어남
int moveCurCnt = 0;
while(true) {
if(nx == 0 && ny ==0) break;
nx = nx+dx[dir];
ny = ny+dy[dir];
if(map[nx][ny] !=0 ) {
moveSand(nx, ny, dir);
}
moveCurCnt++;
if(moveGoalCnt == moveCurCnt) { // 이동해야할 거리를 채우면 방향을 바꾼다.
moveCurCnt = 0;
dir = (dir + 1) % 4;
dirCnt++;
}
if(dirCnt == 2) { // 방향이 2번 바뀔때마다 이동해야할 거리는 1씩 증가한다.
dirCnt = 0;
moveGoalCnt++;
}
}
}
static int dx[] = {0, 1, 0, -1}; // 좌 하 우 상
static int dy[] = {-1, 0, 1, 0};
static int answer;
private static void moveSand(int sx, int sy, int dir) {
int sum = 0;
// 일단 5%
int nx = sx+dx[dir]*2;
int ny = sy+dy[dir]*2;
sum += isBoundResult(nx,ny,sx,sy,5);
// 10%
nx = sx+dx[dir];
ny = sy+dy[dir];
for(int d=1; d<=3; d+=2) {
int nnx = nx+dx[(dir+d)%4];
int nny = ny+dy[(dir+d)%4];
sum += isBoundResult(nnx, nny, sx, sy, 10);
}
// 7% 2%
nx = sx;
ny = sy;
for(int d=1; d<=3; d+=2) {
for(int i=1; i<=2; i++) {
int nnx = nx+dx[(dir+d)%4]*i;
int nny = ny+dy[(dir+d)%4]*i;
if(i == 1)
sum += isBoundResult(nnx,nny, sx, sy, 7);
else
sum += isBoundResult(nnx,nny, sx, sy, 2);
}
}
// 1%
nx = sx - dx[dir];
ny = sy - dy[dir];
for(int d=1; d<=3; d+=2) {
int nnx = nx+dx[(dir+d)%4];
int nny = ny+dy[(dir+d)%4];
sum += isBoundResult(nnx, nny, sx, sy, 1);
}
nx = sx + dx[dir];
ny = sy + dy[dir];
if(isBound(nx, ny)){
map[nx][ny] += map[sx][sy] - sum;
}
else {
answer += map[sx][sy] - sum;
}
map[sx][sy] = 0;
}
private static int isBoundResult(int nx, int ny, int sx, int sy, int per) {
if(isBound(nx, ny)) {
map[nx][ny] += map[sx][sy] * per / 100;
}
else {
answer += map[sx][sy] * per / 100;
}
return map[sx][sy] * per / 100;
}
private static boolean isBound(int nx, int ny) {
if(nx < 0 || nx >= N || ny < 0 || ny >= N) {
return false;
}
return true;
}
}
소요 시간 : 1시간 20분
우선 이동하는 로직은
좌 1
하 1
--------------------
우 2
상 2
--------------------
좌 3
하 3
방향이 2번 전환될 때 마다 이동해야할 거리가 1씩 증가하게됩니다.
dirCnt에 방향 전환한 횟수를 나타냈고
moveGoalCnt에 그 방향에 대해 몇번 이동해야하는지
moveCurCnt 그 방향에 대해 몇번 이동했는지를 나타냈습니다.
다음으로 좌로 갔을 때 모래가 흩뿌려지는 것이랑 하로 갔을 때 흩뿌려지는 위치가 다르기 때문에
좌 하 상 우로 dir 값을 지정했고
좌로 갈 때 dir은 +1, +3 하여 하,우를 계산했습니다.
하로 갈 때도 마찬가지로 +1, +3을 하면 좌,우의 위치이기 때문에 이를 이용했습니다.
이동한 모래가 격자 밖을 벗어나면 answer에 값을 더해주고 벗어나지 않으면 map을 갱신해주었습니다.