Parametric Search 란 이분 탐색을 활용한 기술이다.
이분 탐색은 정렬된 값에서 특정 값의 위치를 빠르게 찾기 위한 알고리즘인데,
이것은 주어진 값에 대해 조건을 만족하는지 검증하는 함수 를 만족하는 가장 크거나 작은 값을 찾기 위한 기술이다.
가 이면 보다 큰 값은 모두 일 것이므로 보다 작은 부분만 탐색하면 되고,
가 이면 보다 작은 값은 모두 일 것이므로 보다 큰 부분만 탐색하면 된다는 개념에서 시작한다.
| 0 | 1 | 2 | 3 | 4 | 5 | 6 |
|---|---|---|---|---|---|---|
| X | X | X | X | O | O | O |
f(x) 의 반환값이 위와 같을 경우 f(x) 를 만족하는 가장 작은 값을 찾기 위해 어떻게 작동하는지 보자.
즉, 인 최솟값 를 찾게 된다.
이 개념을 활용하면 문제에서 특정 조건을 만족하는 최적의 값을 빠르게 찾을 수 있다.
의 시간복잡도를 라고 하면 이분 탐색의 시간복잡도는 이므로
최종 시간복잡도는 이 된다.
예를 들어 의 시간복잡도가 이라면 N = 200,000 까지 가능한 이다.
int parametricSearch() {
int start = 1;
int end = 200000;
int answer = -1;
while(start <= end) {
int mid = (start + end) / 2;
if (func(mid)) { // 반환값이 true 면 왼쪽 구역 탐색 (end = mid - 1)
end = mid - 1;
answer = mid;
} else {
start = mid + 1;
}
}
return answer;
}
func 함수를 이진탐색 내부에서 호출하면서 x 의 최소값을 찾아서 반환하는 코드이다.
start 보다 end 가 작아지면 while 루프를 탈출하고 가장 최근에 true 를 반환했던 mid 값이 answer 에 담겨있으므로 answer 을 반환한다.
문제를 보면 집의 좌표 가 최대 10억이고, 집의 개수 은 20만이다.
집의 좌표가 임의로 주어지기 때문에 직접 가능한지 해보는 수밖에 없다.
모든 집을 순회하며 설치 해보기 isValid() -> = 200,000
이진 탐색을 사용하여 가능한 최대 거리 찾기 -> -> 가 최대 10억이므로 약 30
30 * 200000 = 약 600만으로 시간복잡도가 괜찮다.
import java.util.*;
import java.io.*;
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String[] line = br.readLine().split(" ");
int n = Integer.parseInt(line[0]);
int c = Integer.parseInt(line[1]);
int[] x = new int[n];
for (int i = 0; i < n; i++) {
x[i] = Integer.parseInt(br.readLine());
}
Arrays.sort(x);
int start = 1;
int end = 1_000_000_000;
int ans = -1;
while (start <= end) {
int mid = (start + end) / 2;
if (isValid(n, c, x, mid)) {
ans = mid;
start = mid + 1;
} else {
end = mid - 1;
}
}
System.out.println(ans);
}
private static boolean isValid(int n, int c, int[] x, int dist) {
int count = 1;
int recent = x[0];
for (int i = 1; i < n; i++) {
if (x[i] - recent >= dist) {
count++;
recent = x[i];
}
}
return count >= c;
}
}
주어지는 집 좌표를 우선이 정렬해줬다. 다른 로직과 동시에 작동하지 않아서 정렬하는 의 시간복잡도가 합연산이므로 상관없다.
이 문제는 최대 거리가 작은 쪽이 true, 커지다 보면 false 가 발견되므로
| 1 | 2 | 3 | 4 | 5 | 6 |
|---|---|---|---|---|---|
| O | O | O | X | X | X |
정답 dist의 결과값이 이런 식으로 나타날텐데, 이 때 반환이 true인 가장 큰 값, 즉 3을 찾아야 한다.
때문에 이진 탐색하며 isValid() 의 결과값이 true 이면 오른쪽 구역을, false 면 왼쪽 구역을 탐색해 나갔다.
탐색이 끝나 start > end 가 됐을때, 가장 최근에 true 값을 반환했던 값이 정답이 된다.