백준 2842 집배원 한상덕 java
1. 문제에 대하여 📦
2차원 배열 형태로 건물의 좌표가 주어진다. 여기서 P는 우체국, K는 택배 줘야할 집, .은 공터이다.
또 2차원 배열 형태로 각 좌표당 고도가 주어진다.
또 2차원 배열 형태로 각 좌표당 고도가 주어진다.
집배원 한상덕씨는 우체국에서 시작해 모든 집에 배달을 할 예정이다. 이때 한상덕씨가 건너간 최소 고도와 최대 고도의 차이를 피로도라고 할 때, 모든 집에 배달할 수 있는 최소 고도의 값은 얼마인가?
2. 코드가 나오기까지 🛠️
KEY WORD: 투 포인터 + BFS최소 피로도는 최소 고도 차이를 의미한다. 고도 자체가 그래프에서의 가중치를 뜻한다고 생각할 수 있지만 사실은 아니다. 고도차이가 최소가 되기만 한다면, 노드를 몇 번을 건너던 상관 없기 때문이다.
따라서 해당 문제는 다음과 같이 푼다.
- 고도 값들을 중복 없이 저장해서 오름 차순 정렬한다. (set과 배열, sort 활용)
- 정렬된 고도를 저장한 배열을
heights[]라고 할 때, 해당 배열에서 투 포인터를 운용한다.
(왼쪽 포인터L= 최소 고도, 오른쪽 포인터R= 최대 고도) heights[L]~heights[R]사이에 모든 집을 방문할 수 있다면 L을 한 칸 올려서 고도차를 줄여본다.- 모든 집을 방문할 수 없다면, R을 한 칸 올려서 고도차를 줄인다.
- 위 계산에서 모든 집을 방문할 수 있을 때마다 최소 고도차를 기존 값과 현 고도차를 비교하여 더 작은 값으로 갱신한다.
여기서 2번의 현 고도 안에서 모든 집을 방문할 수 있는지 확인하는데, BFS가 쓰인다.
(1) 시간복잡도 분석 ⏳
N = 50 이므로 K(집)과 중복 없는 고도는 최대 250개가 나올 수 있다.
- 투 포인터 =
- BFS =
둘은 동시에 일어나므로 최종 최악의 시간복잡도는 이다. 하지만 N이 최대 50이므로 충분히 통과한다.
3. 코드 🌐
import java.util.*; import java.io.*; public class Main { static class Coordinate { int r; int c; public Coordinate(int r, int c){ this.r = r; this.c = c; } } static int N, K; static int [][] map; static int [] heights; static int [][] dir = new int [][] {{0,1},{0,-1},{1,0},{-1,0}, {1,1},{1,-1},{-1,-1},{-1,1}}; static Coordinate post_office; static boolean [][] homes; public static void main(String[] args) throws IOException { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); N = Integer.parseInt(br.readLine()); K = 0; map = new int [N][N]; homes = new boolean [N][N]; for(int i = 0; i < N; i++){ String row = br.readLine(); for(int j = 0; j < N; j++){ char now = row.charAt(j); if(now == 'P') post_office = new Coordinate(i,j); if(now == 'K') { homes[i][j] = true; K++; } } } HashSet<Integer> set = new HashSet<>(); for(int i = 0; i < N; i++){ StringTokenizer st = new StringTokenizer(br.readLine()); for(int j = 0; j < N; j++){ map[i][j] = Integer.parseInt(st.nextToken()); set.add(map[i][j]); } } heights = new int [set.size()]; int iter = 0; for(int temp : set){ heights[iter++] = temp; } Arrays.sort(heights); int left = 0; int right = 0; int min = Integer.MAX_VALUE; while(left < heights.length && right < heights.length){ if(left > right) right++; boolean result = bfs(heights[left], heights[right]); if(result) { min = Math.min(min, heights[right] - heights[left]); left++; }else{ right++; } } System.out.println(min); } public static boolean bfs (int low, int high){ if(map[post_office.r][post_office.c] < low || map[post_office.r][post_office.c] > high) return false; int visit_cnt = 0; boolean [][] check = new boolean [N][N]; ArrayDeque<Coordinate> aq = new ArrayDeque<>(); aq.add(new Coordinate(post_office.r, post_office.c)); check[post_office.r][post_office.c] = true; while(!aq.isEmpty()){ Coordinate now = aq.poll(); for(int i = 0; i < 8; i++){ int nr = now.r + dir[i][0]; int nc = now.c + dir[i][1]; if(OOB(nr,nc) || check[nr][nc]) continue; if(map[nr][nc] < low || map[nr][nc] > high) continue; if(homes[nr][nc]) visit_cnt++; if(visit_cnt == K) return true; check[nr][nc] = true; aq.add(new Coordinate(nr,nc)); } } return false; } public static boolean OOB (int r, int c) { return r < 0 || c < 0 || r >= N || c >= N; } }
4. 트러블 슈팅 or 배운 점📝
(1) 틀린 점
해당 문제에서 투포인터가 가리키는 값을 기준으로 BFS를 돌거나 추후 계산을 해야하는데, 자꾸 index 자체를 기준으로 계산하려해서 계속 틀렸다.
(2) 배운 점
플래티넘 문제는 어려운 알고리즘이 아니라, 복수의 알고리즘을 혼합 사용해야 하는 문제가 많다. 따라서 그렇게 겁먹을 필요는 없을 것 같다.
