코드트리 - 나무 박멸(G4)

블랑·2023년 4월 8일
0

SWEA풀이

목록 보기
5/5

문제

https://www.codetree.ai/training-field/frequent-problems/tree-kill-all/description?page=3&pageSize=20&username=

풀이

전형적인 시뮬레이션 문제이다.
queue를 활용하여 문제를 해결하였다.

늘 느끼는 거지만, 시뮬레이션에는 특별한 기술이나 구상 방법보다는 문제가 요구하는 것을 정확하게 이해하고 구현하는 것이 중요할 것 같다.

코드


import java.util.*;

public class 나무박멸 {
	static final int D = 4;
	static int N, M, K, C, res;
	
	static int[][] map; //맵
	static int[][] del; //제초제 여부
	static int[][] board; //시뮬레이션용 보드
	
	static boolean[][] visit;
	static int[][] d = {{0, -1}, {-1, 0}, {1, 0}, {0, 1}};
	static int[][] cross = {{-1, -1}, {-1, 1}, {1, 1}, {1, -1}};
	static Queue<Tree> q = new ArrayDeque<>();
	static Queue<Tree> subQ = new ArrayDeque<>();
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		N = sc.nextInt(); //크기
		M = sc.nextInt(); //진행년도
		K = sc.nextInt(); //제초 범위
		C = sc.nextInt(); //제조 남아 있는 년도
		
		map = new int[N][N];
		del = new int[N][N];
		board = new int[N][N];
		
		//맵 입력받기 / 트리 정보 만들어 넣기
		for (int i = 0; i < N; i++) {
			for(int j = 0; j < N; j ++) {
				map[i][j] = sc.nextInt();
			}
		}
		
		//년도만큼 진행
		for (int i = 0; i < M; i++) {
			grow(); //나무 성장
			make(); //나무 번식
			down(); //제초제 주기 1년 감소
			search(); //가장 많이 제거되는 칸 찾기 && 제초제 뿌리기
			
		}
		
		System.out.println(res);
	}

	private static void down() {
		for (int i = 0; i < N; i++) {
			for (int j = 0; j < N; j++) {
				if(del[i][j] > 0) del[i][j] -= 1;
			}
		}
	}

	private static void search() {
		int max = 0; //최대 제초 값
		int mr = 999, mc = 999; //최대 제초되는 인덱스 (r, c)
		int now;
		// 완전 탐색 시행 : 각 맵마다 제초 범위 찍어보고 최대 값이 있는 좌표 저장
		for (int i = 0; i < N; i++) {
			for (int j = 0; j < N; j++) {
				now = searchTwo(i, j);	//현재 좌표에서 작업했을 때 값 반환
				if(max <= now) {//1. 조건에 맞는다면 최대값 및 수치 갱신
					if(max == now) {
						if(i > mr) continue; 			//2. 탈락 - 행이 더 높음
						if(i == mr && j > mc) continue; //3. 탈락 - 열이 더 높음
					}
					//아니라면 갱신
					max = now; mr = i; mc = j;
				}
			}
		}
		
		//4. 해당 값 넘겨주기
		res += max;
		//5. 해당 좌표 기준으로 제초제 뿌리기 시행
		remove(mr, mc);
	}

	private static void remove(int r, int c) {
		//1. 제초제 투하
		del[r][c] = C; // 현재 위치 제초제 투하
		// 제초제 확산
		if (map[r][c] > 0) {
			for (int j = 0; j < D; j++) {
				for (int i = 1; i <= K; i++) {
					int nr = r + cross[j][0] * i;
					int nc = c + cross[j][1] * i;

					if (cantGo(nr, nc))
						continue;// 인덱스 아웃
					del[nr][nc] = C; // 제초제 투하

					if (map[nr][nc] <= 0)
						break;
				}
			}
		}
		
		//2. 나무 제거
		for (int i = 0; i < N; i++) {
			for (int j = 0; j < N; j++) {
				if(del[i][j] > 0 && map[i][j] > 0) map[i][j] = 0;
			}
		}
	}

	private static int searchTwo(int r, int c) {
		//1. 인자 정비
		int remove = 0; //리턴값
		clean(); //계산해볼 보드의 내부값 0으로 초기화
		
		//2. 나무가 있다면 최대 가산치 K만큼 확산 가능, 아닐 경우 거기까지만
		board[r][c] = C; 	//현재 위치 제초제 투하
		//현재 위치가 나무가 아니라면 해당 위치만 투하하고 끝
		if(map[r][c] > 0) {	
			for (int j = 0; j < D; j++) {
				for (int i = 1; i <= K; i++) {
					int nr = r + cross[j][0] * i;
					int nc = c + cross[j][1] * i;
					
					if(cantGo(nr, nc)) continue;//인덱스 아웃
					board[nr][nc] = C; 			//제초제 투하
					
					if(map[nr][nc] <= 0) break;
				}
			}
		}
		//3. board에 저장된 값으로 최대값 계산
		for (int i = 0; i < N; i++) {
			for (int j = 0; j < N; j++) {
				//제초제 있는 칸일 경우,그리고 그 칸에 나무가 있다면
				if(board[i][j] > 0 && map[i][j] > 0) remove += map[i][j]; //값 가산  
			}
		}
		
		return remove; //제초되는 수 반환
	}

	private static void clean() {
		for (int i = 0; i < N; i++) {
			for (int j = 0; j < N; j++) {
				board[i][j] = 0;
			}
		}
	}

	private static void make() {
		//번식 진행 : 인접한 4개 칸 중 나무, 제초제 전부 없는 칸 개수 확인 및 해당 칸 queue에 담아 저장
		//수치 : 현재 칸 / 번식되는 칸
		
		//1. 일단 완전탐색으로 나무 위치 찾음
		for (int i = 0; i < N; i++) {
			for (int j = 0; j < N; j++) {
				if(map[i][j] > 0) makeTwo(i, j); //2. 찾은 값 넘겨주고 번식 위한 단계 진행
			}
		}
		
		//3. 큐에 저장된 수치만큼 전부 가산
		while(!q.isEmpty()) {
			Tree now = q.poll();
			map[now.r][now.c] += now.val;
		}
	}

	private static void makeTwo(int r, int c) {
		//1. 사방탐색으로 번식이 가능한 칸의 개수를 찾음
		int spot = 0; //번식 가능한 칸
		for (int i = 0; i < D; i++) {
			int nr = r + d[i][0];
			int nc = c + d[i][1];
			//인덱스 제거, 맵이 빈칸일 경우, 해당 위치에 제초제가 없을 경우
			if(cantGo(nr, nc) || map[nr][nc] != 0 || del[nr][nc] != 0) continue;
			spot += 1; //위치 증가
			subQ.offer(new Tree(nr, nc)); //일단 수치 없이 위치만 저장해놓음
		}
		
		
		
		//2. 수치 계산 후 이를 메인 큐에 다시 집어넣음. = 해당 저장값은 모든 연산이 끝난 후 일괄 가산
		while(!subQ.isEmpty()) {
			int val = map[r][c] / spot; //번식할 수치
			Tree temp = subQ.poll();
			temp.val = val; //번식 수치 넣음
			q.offer(temp);
		}
	}

	private static void grow() {
		//1. 나무 집어넣기
		for (int i = 0; i < N; i++) {
			for (int j = 0; j < N; j++) {
				if(map[i][j] > 0) q.offer(new Tree(i, j, map[i][j])); //트리 정보 넣기
			}
		}
		
		//2. 큐에서 나무가 없어질 때까지 나무 꺼내기
		//꺼낸 나무에서 사방 탐색 진행하여 나이가 얼마나 먹을 지 확인 및 적용
		while(!q.isEmpty()) {
			Tree now = q.poll();
			int age = 0;
			for (int i = 0; i < D; i++) {
				int nr = now.r + d[i][0];
				int nc = now.c + d[i][1];
				if(cantGo(nr, nc) || map[nr][nc] <= 0) continue;
				age += 1;
			}
			
			map[now.r][now.c] += age;
		}
	}

	static boolean cantGo(int r, int c) {
		if(r < 0 || c < 0 || r >= N || c >= N) return true;
		return false;
	}
	static class Tree {
		int r, c;
		int val;
		public Tree(int r, int c) {
			super();
			this.r = r;
			this.c = c;
		}
		public Tree(int r, int c, int val) {
			super();
			this.r = r;
			this.c = c;
			this.val = val;
		}
		
		
	}
}
profile
안녕하세요.

0개의 댓글