알고리즘

[백준] 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을 갱신해주었습니다.