목차
문제 : boj19845
필요 알고리즘
- 이분 탐색
- 의외로 이분 탐색 문제이다! 물론 다른 방법들도 있다.
※ 제 코드에서 왜 main 함수에 로직을 직접 작성하지 않았는지, 왜 Scanner를 쓰지 않고 BufferedReader를 사용했는지 등에 대해서는 '자바로 백준 풀 때의 팁 및 주의점' 글을 참고해주세요. 백준을 자바로 풀어보려고 시작하시는 분이나, 백준에서 자바로 풀 때의 팁을 원하시는 분들도 보시는걸 추천드립니다.
풀이
문제 설명이 좀 복잡해서 그렇지 잘 생각해보면 해야하는건 단순한 편이다.
배열 arr에다가 arr[1]부터 arr[n] 까지에 입력으로 주어진 N개의 정수 a를 순서대로 담아두었다고 해보자.
우선 오른쪽으로 나가는 레이저로 몇 마리가 제거되는지만 생각해보자. 이 경우 입력으로 들어온 (x,y)값을 기준으로 arr[y] - x+1 을 하면 될 것이므로 이건 쉽게 생각할 수 있다. 수식을 말로 설명해보면 "현재 층에 arr[y] 마리가 존재하는데 레이저가 x번째부터 시작되므로, 현재 층에 존재하는 마리수 중 x번째 이상에 있던 녀석들이 제거된다"는 의미이다.
그럼 위로 향하는 레이저는 어디까지 넴모들을 제거할까?
일단 이걸 어떻게 해결할지 알고리즘을 먼저 생각하기보다는, 그냥 단순하게 직관적으로 생각해보면 입력으로 들어온 x값보다 더 작은 수(= x-1 이하)가 살고있는 층이 몇층인지 알아내면 된다! 예를들어 아래와 같은 상황이라면 당연히 x=3인 레이저는 arr에 들어있는 값이 3 이상인 곳에만 영향을 끼칠 수 있으므로, arr에 들어있는 값이 3 미만으로 바뀌는 위치를 찾아내면 그 층 직전까지 제거되는 것이다.
하지만 매번 arr을 순회하면서 찾게 되면 O(NQ)가 되므로 통과할 수 없다. 그러므로 이제 알고리즘을 생각해봐야하는데, 뭐 펜윅이나 세그 등을 써도 되겠지만 어차피 입력은 항상 내림차순으로 들어오므로 그냥 이분 탐색으로 처리하면 된다. 다만 정확히 특정 값을 찾으라는 식으로 이분 탐색을 할 순 없고, c++에 있는 lower_bound 와 비슷하게 탐색을 해야한다(특정 값보다 작은 값). 자바에도 그게 있는데, TreeSet이나 TreeMap의 lower 함수를 쓰면 된다(미만이 아니라 이하를 탐색하려면 floor).
이 때 TreeSet으로 처리하려면 커스텀 정렬을 구하기가 상당히 어려워져서, TreeMap으로 key는 arr[인덱스], value는 인덱스로 해주면, lower을 통해 특정값 미만의 인덱스 위치를 알 수 있게 된다. 주의점은 특정값 미만인 '가장 작은 인덱스'를 찾아야 한다는 점이다. 즉, arr = [3, 3, 2, 2, 2] 일 경우 3 미만을 찾는다고 하면 결과가 2가 나와야지(처음 3미만인 값으로 바뀐 인덱스), 3이나 4가 나오면 안된다.
int[] arr = new int[n+1];
for (int i = 1; i <= n; i++) {
arr[i] = Integer.parseInt(st.nextToken());
if (tm.containsKey(arr[i])) continue; // 해당 값의 최소 인덱스를 유지하기 위한 부분
tm.put(arr[i], i); // TreeMap에 넣기
}
StringBuilder sb = new StringBuilder();
while (q-->0) {
st = new StringTokenizer(br.readLine());
int c = Integer.parseInt(st.nextToken());
int r = Integer.parseInt(st.nextToken());
if (c > arr[r]) { // 이미 arr[y]보다 x가 크다면 무시
sb.append(0).append('\n');
continue;
}
// arr[r]-c+1 은 우측으로 제거되는 부분.
// tm.lowerEntry(c).getValue()-1-r 은 위쪽으로 제거되는 부분.
sb.append(arr[r]-c+1+tm.lowerEntry(c).getValue()-1-r).append('\n');
}
코드 : github
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
import java.util.TreeMap;
public class Main {
static BufferedReader br = new BufferedReader(new InputStreamReader(System.in), 1<<16);
public static void main(String[] args) throws Exception {
new Main().solution();
}
private void solution() throws Exception {
StringTokenizer st = new StringTokenizer(br.readLine());
int n = Integer.parseInt(st.nextToken());
int q = Integer.parseInt(st.nextToken());
TreeMap<Integer, Integer> tm = new TreeMap<>();
tm.put(0, n+1);
st = new StringTokenizer(br.readLine());
int[] arr = new int[n+1];
for (int i = 1; i <= n; i++) {
arr[i] = Integer.parseInt(st.nextToken());
if (tm.containsKey(arr[i])) continue;
tm.put(arr[i], i);
}
StringBuilder sb = new StringBuilder();
while (q-->0) {
st = new StringTokenizer(br.readLine());
int c = Integer.parseInt(st.nextToken());
int r = Integer.parseInt(st.nextToken());
if (c > arr[r]) {
sb.append(0).append('\n');
continue;
}
sb.append(arr[r]-c+1+tm.lowerEntry(c).getValue()-1-r).append('\n');
}
System.out.print(sb);
}
}
'PS > BOJ' 카테고리의 다른 글
[자바] 백준 16491 - 대피소 찾기 (java) (0) | 2023.07.24 |
---|---|
[자바] 백준 16965 - 구간과 쿼리 (java) (2) | 2023.07.23 |
[자바] 백준 2374 - 같은 수로 만들기 (java) (0) | 2023.07.20 |
[자바] 백준 1490 - 자리수로 나누기 (java) (0) | 2023.07.20 |
[자바] 백준 2632 - 피자판매 (java) (0) | 2023.07.20 |
댓글