https://www.acmicpc.net/problem/11663
코드설명
이분탐색(BinarySearch) 을 활용합니다.
처음에 먼저 떠올린 간단한 방법은 누적합(PrefixSum) + 메모리초과(OutOfMemory)입니다. 누적합을 활용하면 10^9 개의 메모리 공간이 필요해지면서 메모리 초과가 발생했습니다.
이분탐색을 활용하여, 하나의 숫자를 찾는데 logn 시간 내에 처리할 수 있습니다.
이 문제에서 가장 중요한점은,
1. left 값은 선분의 시작점입니다.
이 left값 보다 크거나 같은 첫번쨰 점을 찾아야 하므로lowerbound로 처리합니다. lowerbound는 주어진 값 이상인 첫번쨰 원소의 위치를 반환합니다.
2. right 값은 선분의 끝점입니다.
이 right 값보다 큰 첫번째 점을 찾아야 합니다.
문제 예시로보면 아래 예시에서 3 30 인 경우 3은 1, 30은 5 가 반환되어야 한다는 의미입니다.
따라서 upperbound를 사용하여, 주어진 값보다 큰 첫번쨰 원소의 위치를 반환합니다.
입력예시 1
5 5
1 3 10 20 30
1 10
20 60
3 30
2 15
4 8
디버깅
leftPos:0rightPos:3
leftPos:3rightPos:5
leftPos:1rightPos:5
leftPos:1rightPos:3
leftPos:2rightPos:2
3
2
4
2
0
따라서,
- Lower Bound(left)는 선분에 포함되는 첫 번째 점의 인덱스를 반환합니다.
- Upper Bound(right)는 선분에 포함되는 마지막 점의 다음 인덱스를 반환합니다.
그래서 Upper Bound(right) - Lower Bound(left)를 계산하면 선분에 포함되는 점의 개수를 정확히 구할 수 있습니다.
조금 더 쉽게 설명하면,
1. lower bound는 찾고자 하는 값 이상(즉, 같거나 큰값)이 처음 나타나는 위치입니다.
2. upper bound는 찾고자 하는 값보다 큰 값(즉, 반드시 큰 값, 만약 범위를 벗어난다면 다음 범위를 반환)이 처음으로 나타나는 위치입니다.
대부분의 이분탐색의 경우 lower bound를 사용한다고 생각하면 되겠습니다.
이 문제의 경우 구간을 구하는 문제이기에 upper bound를 사용했어야했구요.
코드
lowerbound와 upperbound를 활용하여 깔끔하게 풀 수 있습니다.
package Main;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.HashSet;
import java.util.StringTokenizer;
public class Main {
private static int N, M, C, T, K;
private static int answer = 0;
private static int[] arr;
private static long[] cache;
private static int Q;
private static StringBuilder sb = new StringBuilder();
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
N = Integer.parseInt(st.nextToken());
M = Integer.parseInt(st.nextToken());
st = new StringTokenizer(br.readLine());
arr = new int[N];
for(int i=0;i<N; i++) {
arr[i] = Integer.parseInt(st.nextToken());
}
Arrays.sort(arr);
for(int i=0;i<M; i++) {
st = new StringTokenizer(br.readLine());
int left = Integer.parseInt(st.nextToken());
int right = Integer.parseInt(st.nextToken());
int rightPos = binarySearchUpperBound(0, N-1, right);
int leftPos = binarySearchLowerBound(0, N-1, left);
sb.append(rightPos - leftPos).append("\n");
}
System.out.println(sb);
}
private static int binarySearchLowerBound(int lo, int hi, int target) {
int left = lo - 1;
int right = hi + 1;
while(left + 1 < right) {
int mid = (left + right) / 2;
if(arr[mid] < target) {
left = mid;
}else if(arr[mid] >= target) {
right = mid;
}
}
return right;
}
private static int binarySearchUpperBound(int lo, int hi, int target) {
int left = lo - 1;
int right = hi + 1;
while(left + 1 < right) {
int mid = (left + right) / 2;
if(arr[mid] <= target) {
left = mid;
}else if(arr[mid] > target) {
right = mid;
}
}
return right;
}
}
이분탐색으로 푼 코드입니다. 이 코드의 경우 upperbound와 lowerbound를 완전히 이해하고 풀지는 못했습니다.
그렇기 때문에, 출력 부분에 여러 제약조건을 처리함을 알 수 있습니다.
package Main;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.HashSet;
import java.util.StringTokenizer;
public class Main {
private static int N, M, C, T, K;
private static int answer = 0;
private static int[] arr;
private static long[] cache;
private static int Q;
private static StringBuilder sb = new StringBuilder();
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
N = Integer.parseInt(st.nextToken());
M = Integer.parseInt(st.nextToken());
st = new StringTokenizer(br.readLine());
arr = new int[N];
for(int i=0;i<N; i++) {
arr[i] = Integer.parseInt(st.nextToken());
}
Arrays.sort(arr);
for(int i=0;i<M; i++) {
st = new StringTokenizer(br.readLine());
int left = Integer.parseInt(st.nextToken());
int right = Integer.parseInt(st.nextToken());
int rightPos = binarySearch(0, N-1, right);
int leftPos = binarySearch(0, N-1, left);
if(rightPos < N && arr[rightPos] == right) {
// System.out.println(rightPos - leftPos + 1);
sb.append(rightPos - leftPos + 1).append("\n");
}else {
// System.out.println(rightPos - leftPos);
sb.append(rightPos - leftPos).append("\n");
}
}
System.out.println(sb);
}
private static int binarySearch(int lo, int hi, int target) {
int left = lo - 1;
int right = hi + 1;
while(left + 1 < right) {
int mid = (left + right) / 2;
if(arr[mid] < target) {
left = mid;
}else if(arr[mid] >= target) {
right = mid;
}
}
return right;
}
}
처음에 문제를 보고, 누적합으로 새로운 점이 생길떄마다 1씩 누적합시키는 방식으로 진행했습니다.
하지만, 좌표의 최대값이 1e9, 10억이기에 메모리초과가 발생합니다.
package Main;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.HashSet;
import java.util.StringTokenizer;
public class Main {
private static int N, M, C, T, K;
private static int answer = 0;
private static int[] arr;
private static long[] cache;
private static int Q;
private static StringBuilder sb = new StringBuilder();
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
N = Integer.parseInt(st.nextToken());
M = Integer.parseInt(st.nextToken());
st = new StringTokenizer(br.readLine());
arr = new int[N];
for(int i=0;i<N; i++) {
arr[i] = Integer.parseInt(st.nextToken());
}
int[] prefixSum = precalc();
for(int i=0;i<M; i++) {
st = new StringTokenizer(br.readLine());
int left = Integer.parseInt(st.nextToken());
int right = Integer.parseInt(st.nextToken());
System.out.println(prefixSum[right] - prefixSum[left - 1]);
}
}
private static int[] precalc() {
int[] prefixSum = new int[100];
for(int i=0; i<N; i++) {
int start = arr[i];
int beforeSum = start == 0 ? 0 : prefixSum[start-1];
int next = i == N - 1 ? 100 : arr[i + 1];
for(int j=start; j<next; j++) {
prefixSum[j] = beforeSum + 1;
}
}
return prefixSum;
}
}
'알고리즘 > binary_search' 카테고리의 다른 글
[백준] 15810 풍선 공장 - 파라매트릭서치(Paramatric Search) JAVA (0) | 2024.09.17 |
---|---|
[백준] 13072 이상한 술집 - 파라매트릭 서치(ParamatricSearch) JAVA (0) | 2024.09.05 |
[백준] 1166 선물 - 파라매트릭 서치(Paramatric Search) JAVA (0) | 2024.08.13 |
[백준] 1515 수 이어 쓰기 - 파라매트릭 서치(Paramatric Search) + 부분 수열(SubSequence) JAVA (0) | 2024.07.20 |
[백준] 2343 기타 레슨 - 파라매트릭 서치(Paramatric Search) JAVA (0) | 2023.11.22 |