[JAVA][백준 16236] 아기상어

SHORTCUT

    반응형
    사이트명 백준
    문제 링크 www.acmicpc.net/problem/16236
    주요 알고리즘 시뮬레이션, BFS,
    사용 언어 Java 자바
    알고리즘이 낯선 사람들을 위해 생각 노트를 같이 두었습니다.

    1. 문제

     

    2. 입력 / 출력

     

     


    3. 풀이

    3-1. 생각 노트

    골드4의 아기상어 문제.

    일단 삼성의 주요 기출 형태(구현) 중 하나이다. 조합같은 개념이 적용되진 않아서 어려운 편은 아니다.

    그저 문제를 잘 읽고, 그대로 잘 구현 하면 된다.

     

    상어는 물고리르 탐색하고 -> 식사 가능한걸 먹는다!

     

     

    3-2. 변수명

    변수명 설명
    Class Fish 물고기들의 시간값(거리)를 기록하기 위한 객체

    ArrayList<Fish> feedList

    식사 가능한 경우 들어가는 후보 먹이 리스트! 추후 정렬을 통해 어느것을 먹을지 결정한다.

    Queue<Fish> q

    이동 가능한 경우에 들어가는 이동 대기열!

     

    3-3. 코드 보기

    import java.io.BufferedReader;
    import java.io.InputStreamReader;
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.Comparator;
    import java.util.LinkedList;
    import java.util.Queue;
    
    public class BOJ16236_아기상어 {
    
    	// 먹이가 될 물고기
    	private static class Fish {
    		int x, y, time;
    
    		public Fish(int x, int y, int time) {
    			super();
    			this.x = x;
    			this.y = y;
    			this.time = time;
    		}
    	}
    	
    	static int N;
    	static int[][] map;
    	static boolean[][] isVisit;
    	
    	static Fish shark;		// 상어도 물고기! 상어의 위치를 기록
    	static int size = 2;
    	static int eaten = 0;
    	
    	static ArrayList<Fish> feedList = new ArrayList<>();
    	static int answer;
    	
    	static int[] dx = {-1, 1, 0, 0};
    	static int[] dy = {0, 0, -1, 1};
    
    	public static void main(String[] args) throws Exception {
    		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    		
    		String[] input = br.readLine().split(" ");
    		N = Integer.parseInt(input[0]);
    		
    		map = new int[N][N];
    		isVisit = new boolean[N][N];
    		
    		for (int i = 0; i < N; i++) {
    			input = br.readLine().split(" ");
    			
    			for (int j = 0; j < N; j++) {	
    				map[i][j] = Integer.parseInt(input[j]);
    				
    				if(map[i][j] == 9) {
    					shark = new Fish(i, j, 0);
    					map[i][j] = 0;
    				}
    			}
    		}
    		
    		simulation();
    		System.out.println(answer);
    	}
    
    	private static void simulation() {
    		
    		Queue<Fish> q = new LinkedList<>();
    		q.add(shark);
    		isVisit[shark.x][shark.y] = true;
    		
    		while(true) {
    			while(!q.isEmpty()) {
    				Fish now = q.poll();
    				int time = now.time;
    
    				
    				for (int k = 0; k < 4; k++) {
    					int nx = now.x + dx[k];
    					int ny = now.y + dy[k];
    					
    					// 범위 밖 -> 아웃
    					if(!(0 <= nx && nx < N && 0 <= ny && ny < N) || isVisit[nx][ny]) continue;
    					
    					// [식사 가능] 먹이 리스트에 추가
    					if(map[nx][ny] < size && map[nx][ny] != 0) {
    						q.add(new Fish(nx, ny, time+1));
    						isVisit[nx][ny] = true;
    						feedList.add(new Fish(nx, ny, time+1));
    					}
    					
    					// [이동만 가능] 사이즈랑 동일 OR 비어있음(0)
    					if(map[nx][ny] == size || map[nx][ny] == 0) {
    						q.add(new Fish(nx, ny, time+1));
    						isVisit[nx][ny] = true;
    					}
    				}
    			}
    			
    			if(!feedList.isEmpty()) {
    				eat();
    				// 식사가 끝났으면, 방문 배열 초기화
    				
    				q.clear();
    				isVisit = new boolean[N][N];
    
    				// 다시 이동하는 큐(q)에다가 현재 상어 추가
    				q.add(shark);
    				isVisit[shark.x][shark.y] = true;
    			}else {
    				return;
    			}
    		}
    		
    	}
    
    	// 먹이 리스트가 값이 존재하면, 식사 시작
    	private static void eat() {
    		
    		// 정렬을 통해 우선순위별로 먹이 리스트 재정렬
    		Collections.sort(feedList, new Comparator<Fish>() {
    			@Override
    			public int compare(Fish o1, Fish o2) {
    				if(o1.time == o2.time) {
    					if(o1.x == o2.x) {
    						if(o1.y > o2.y) {
    							return 1;
    						}else {
    							return -1;
    						}
    					}else {
    						if(o1.x > o2.x) {
    							return 1;
    						}else {
    							return -1;
    						}				
    					}
    				}else if(o1.time > o2.time){
    					return 1;
    				}else {
    					return -1;
    				}
    			};
    		});
    
    		Fish now = feedList.get(0);
    		
    		// 상어 위치 이동, 상어가 먹은 횟수 증가, 상어 걸린 시간 증가
    		shark.x = now.x;
    		shark.y = now.y;
    		
    		if(++eaten == size) {
    			size++;
    			eaten = 0;
    		}
    		
    		answer += now.time;
    		
    		// 지도 위에 해당 위치 0 처리
    		map[now.x][now.y] = 0;
    		
    		// 먹이 리스트 초기화
    		feedList.clear();
    	}
    
    }
    

     

    3-4. 마무리

    이전에 한번 푼 문제였는데, 다시 푸니 틀렸다...

     

    틀린 이유 분석

     - BFS를 수행할 때, time+1로 시간값이 증가하므로 4방 탐색을 하는 단계마다 먹이 리스트에 넣으면 거리(시간)계산을 안해도 되겠다는 생각으로 접근

     - 하지만, 몇몇 값에선, time값이 늦게 반영되어 동일한 시간값이지만 체킹을 못하고 넘어가는 경우가 발생..

     - 아, 식사 가능한 경우에도 먹이 리스트에 넣고 + 이동 Q에도 넣어야 한다. 이부분 살짝 애매하면 꼬인다.

     

    따라서, 로직 자체를 while로 무한루프를 만들고 [BFS 탐색 -> 식사] 를 반복하는 형태로 변경

     

    BFS는 BFS로만 두고, 로직을 끼우지 말자. 딱 탐색이라는 기능만 구현해두는게 맞다.

     

     

    아. 

    이전 코드를 다시 보는데, 내가 썼던 변수명이나 스타일이 똑같았다..

    역시 사람 안변한다는 걸 느낀다.. 아니, 변하진 않고 더 발전하겠지..

     

     

     

    반응형

    댓글

    Designed by JB FACTORY