Binary Search & Parametric Search
이분 탐색은 원하는 값을 찾을 때, 완전탐색보다 더 빠르게 찾을 수 있는 알고리즘입니다.
일반적인 탐색보다는 나중에 매개 변수 탐색에 더 많이 사용되지만, 기본적인 탐색도 알아두면 좋습니다.
먼저 정렬을 해 주는 것이 중요합니다. 그 이유는 중앙값을 기준으로 값을 찾아 나갈 것인데, 정확한 기준이 없다면 중앙값을 어떻게 바꿔 나가야 하는지를 모르기 때문입니다.
그 다음! 양끝 지점을 잡고, 그 중앙값에 따라 판별을 합니다. 이 판별은 두가지 경우로 나뉘게 됩니다.
1. 찾는 값이 중앙값보다 큰 경우 - 작은 지점을 중앙값으로 갱신합니다.
2. 찾는 값이 중앙값보다 작은 경우 - 큰 지점을 중앙값으로 갱신합니다.
이렇게 판별하는 이유는 중앙에 있는 값보다 작다면? 굳이 더 큰 수들을 볼 필요가 없기 때문입니다. 큰 경우에도 마찬가지로 중앙값보다 작은 수는 볼 필요가 없게 되기 때문입니다.
이런 과정을 반복하면서 답을 찾으면 됩니다.
예를 들어,
4 1 5 2 3 중에서 2를 찾으려 한다면, 우선 정렬을 한 다음에 탐색을 진행합니다.
정렬의 이유를 설명하자면, 중앙값이 5인 상태에서는 원하는 값이 더 작은지, 큰지를 판별할 수 없기 때문입니다.
hi = 5, lo = 1로 설정하고 탐색을 진행합니다.
정렬을 한 다음에 중앙에는 3이 존재하게 됩니다. 3보다 큰 수에는 절대로 2가 존재할 수 없으므로 hi를 중앙인덱스로 교체시킵니다. 그럼 현재 lo = 1, hi = 3이 되게 됩니다.
그런 다음 한번 더 탐색을 진행합니다. 이 때 중앙에는 2가 존재하게 되는데 우리가 찾던 수이므로 탐색을 멈춥니다.
구현은 다음과 같이 할 수 있습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
/*
ID : litterhy1
LANG : C++
*/
#include <iostream>
#include <algorithm>
using namespace std;
int a[100010];
int main()
{
int n; cin >> n;
for(int i=1; i<=n; i++) cin >> a[i];
sort(a+1, a+n+1);
int m; cin >> m;
while(m--) {
int t; cin >> t;
int lo = 1, hi = n, flag = 0;
while(lo <= hi) {
int mid = (lo+hi)/2;
if(a[mid] == t) {
flag = 1;
break;
}
if(a[mid] < t) {
lo = mid+1;
} else {
hi = mid-1;
}
}
cout << flag << "\n";
}
return 0;
}
|
cs |
이제 이걸 이용해서 매개 변수 탐색을 해 볼 건데,
처음하면 상당히 어렵습니다. 그래도! 몇번 짜다보면 이해할 수 있으니 포기하진 마세요~!
https://www.acmicpc.net/problem/1654
-- 매개 변수 탐색(parametric search)으로 풀 수 있는 예시용 문제입니다.
매개 변수 탐색은 원하는 값이 나올 때까지 수를 몇개 넣어보고 조건에 맞는 것을 출력하는 방법입니다. 구현 방법은 이분탐색과 크게 다르지 않습니다.
위 문제에서 최적화된 값을 찾기 위해,
제일 큰 값과 제일 작은 값을 설정하고 중앙을 이분탐색을 하면서 최적해를 찾아갑니다.
lo = 0, hi = 10^18 ( 범위 계산하기 귀찮으니 그냥 엄청 큰 값을 때려 넣음. )
그런 다음에 중앙값으로 배열의 모든 값을 나누면서 만들어진 랜선의 수가 m일 때 lo를 출력하면 됩니다.
그럼 조건을 만족하는 최소 해를 구할 수 있습니다.
구현은 다음과 같이 할 수 있습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
/*
ID = litterhy1
LANG = C++
*/
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long int INT;
int a[1000010];
int main()
{
int n, m; cin >> n >> m;
for(int i=0; i<n; i++) cin >> a[i];
sort(a, a+n);
INT lo = 0, hi = 1e18;
while(lo+1 < hi) {
INT mid = (lo+hi)/2;
INT sum = 0;
for(int i=0; i<n; i++)
sum += a[i]/mid;
if(sum >= m) {
lo = mid;
} else {
hi = mid;
}
}
cout << lo << "\n";
return 0;
}
|
cs |
추천 문제
https://www.acmicpc.net/problem/10816
이분 탐색으로 원하는 수가 몇개 들어있는지 구하면 되는 문제입니다.
보통 map을 사용하여 원하는 값이 몇개 있는지를 체크해 두고 찾으면 위 문제와 크게 다르지 않습니다.
https://www.acmicpc.net/problem/2805
매개 변수 탐색으로 해결할 수 있는 문제입니다.
M보다 작거나 같은 경우에는 자를 수 없으므로 아무 것도 하지 말고, M보다 큰 경우에는 중앙값을 빼고 다 더해서 원하는 값이 나올 때까지 돌리면 됩니다.
https://www.acmicpc.net/problem/2512
매개 변수 탐색으로 풀 수 있는 문제입니다.
중앙값보다 크면 중앙값을 더하고 아니라면 배열의 값을 더하면서 탐색을 진행합니다.
추가적으로 다 더한 값이 M보다 작다면, 최대값을 출력해 주는 것을 처리해 주면 해결할 수 있습니다.