Lower bound
선형구조의 부분탐색법인 이분탐색은 찾고자 하는 값이 없으면(맨 마지막 값) 탐색 실패!
하지만,
lower bound는 찾고자 하는 값 이상이 처음 나타나는 위치입니다. lower bound의 경우에는 같은 원소가 여러개 있더라도 상관없습니다. 찾고자 하는 값 이상의 값이 처음 나타나는 위치를 찾아내기 위해, 이분탐색 방법에서의 조건을 조금 변경하면 됩니다.
[문제] n개로 이루어진 정수 집합에서 원하는 수 k이상인 수가 처음으로 등장하는 위치를 찾으시오. 단, 입력되는 집합은 오름차순으로 정렬되어 있으며(이분 탐색 가능), 같은 수가 여러개 존재할 수 있다.
입력) 첫 줄에 한 정수 n이 입력된다. 둘째 줄에 n개의 정수가 공백으로 구분되어 입력된다. 셋째 줄에는 찾고자 하는 값 k가 입력된다. (단, 2 <= n <= 1,000,000, 각 원소의 크기는 100,000,000을 넘지 않는다.)
출력) 찾고자 하는 원소의 위치를 출력한다. 만약 모든 원소가 k보다 작으면 n+1을 출력한다.
입력 예 |
출력 예 |
5 7 |
4 |
8 1 2 3 5 7 9 11 15 6 |
5 |
5 1 2 3 4 5 7 |
6 |
해결방법) 먼저, 데이터가 입력되어있는 배열을 A[], 찾고자 하는 값을 k, lower bound를 찾고자 하는 구간을 [s, e]로 설정하면, 구간 내의 중간 위치를 m이라고 할때, A[m-1] < k이면서 A[m] >= k를 만족하는 최소 m을 찾는 문제가 된다.
1 3 5 7 7 에서 7을 탐색할 때, 7과 같거나 큰 수가 나오는 첫 위치가 바로 lower bound!
이때, m은 2이상인 값이 되는데, 일반적인 이분탐색 방법에서 A[m] == k인 부분을 포함시키면 된다.
또한, 모든 원소가 k보다 작을 때에는 n+1을 출력해야 하므로, 처음 구간을 잡을 때, [1, n]을 잡는 대신 [1, n+1]을 잡아야 한다.
데이터를 A[] 배열에 모두 입력 받은 상태에서 탐색 준비를 한다. 찾아야 할 데이터 k(6)와 같거나 큰 가장 첫 위치 lower bound, 탐색 구간의 시작위치와 마지막 위치는 s=0, e=7+1이 되고, 중간 위치를 계산하면 (0+8)/2 = 4가 된다.
A[4]의 값(7)이 찾고자 하는 6보다 크므로, 탐색 구간을 [0,4]로 바꾼다. 일반적인 이분탐색이었다면 탐색 구간을 [0,3]으로 바꿔야 하지만, lower bound는 k이상의 값이 나타나는 최소위치이므로, 마지막 위치 (e)까지 포함 시켜야 한다. [0,4]의 중간 위치는 (0+4)/2 = 2가 된다.
A[2]의 값(3)이 찾고자 하는 6보다 작으므로, 탐색 구간을 [3,4]로 바꾸고 다시 탐색해 나간다. [3, 4]의 중간 위치는 (3+4)/2=3이 된다.
이제 더이상 탐색 구간을 줄일 수 없으므로, 위치 4가 k이상인 원소의 최초 위치가 된다.
#include <stdio.h> int n, k, A[1000001]; int solve(int s, int e) { int m; while(e-s > 0) { m = (s+e)/2; if(A[m] < k) s=m+1; else e = m; } return e+1; } int main() { scanf("%d", &n); for(int i=0; i < n; i++) scanf("%d", A+i); scanf("%d", &k); printf("%d\n", solve(0, n)); return 0; }
6: 주어진 범위 [s,e]에서 참색하도록 한다. s==e이면 반복 종료
8: 주어진 범위의 중간 위치를 계산한다.
9: 찾고자 하는 값보다 작으면 오른쪽으로 한 칸만 더 시작 구간 갱신
10: 찾고자 하는 값보다 크면 거기까지 끝 구간 갱신
12: 찾는 구간에 없는 경우 가장 마지막+1 위치 전달
lower_bound는 이분탐색과 비슷하지만 끝의 경계조건을 처리하기가 매우 까다롭습니다. 따라서 실제 정보올림피아드 대회에서는 lower_bound를 활용하기 위해서 stl라이브러리인 알고리즘에 포함된 함수를 사용합니다.
lower_bound(): <algorithm>에 포함되어있는 함수로, n개의 데이터로 채워진 A[] 배열에서 비교기준(compare)에 따라서 lower_bound의 위치를 계산해 줍니다.
std::lower_bound(A, A+n, k, [compare])
[compare] 부분을 생략하는 경우에는 기본적으로 오름차순으로 정렬한 상태라고 가정했을 때의 lower bound 위치를 계산해 줍니다.
#include <stdio.h> #include <algorithm> int n, k, A[1000001]; int main() { scanf("%d", &n); for(int i=0; i<n; i++) scanf("%d", A+i); scanf("%d", &k); printf("%d\n", std::lower_bound(A, A+n, k)-A+1; }
12: std::lower_bound(A, A+n, k)는 어떤 위치, std::lower_bound(A, A+n, k)-A+1은 A배열에서의 상대적 위치(인덱스)
Upper bound
lower bound는 찾고자 하는 값 이상이 처음으로 나타나는 위치인 반면에, upper bound는 찾고자 하는 값보다 큰 값이 처음으로 나타나는 위치입니다.
입력 예 |
Lower bound |
Upper Bound |
5 1 3 5 7 7 7 |
4 |
6 ( 7을 초과한 값 ) |
[문제] n개로 이루어진 정수 집합에서 원하는 수 k를 초과하는 수가 처음으로 등장하는 위치를 찾으시오. 단, 입력되는 집합은 오름차순으로 정렬되어 있으며, 같은 수가 여러 개 존재할 수 있다.
입력) 첫 줄에 한 정수 n과 찾고자 하는 값 k가 공백으로 구분되어 입력되고, 둘째 줄에 n개의 정수가 공백으로 구분되어 입력된다. (단, 2 <= n <= 1,000,000, 각 원소의 크기는 100,000,000을 넘지 않는다)
출력) 찾고자 하는 원소의 위치를 출력한다. 만약 모든 원소가 k보다 작으면 n+1을 출력한다.
입력 예 | 출력 예 |
5 7 | 5 |
8 1 2 3 5 7 9 11 15 6 | 7 |
5 1 2 3 4 5 7 | 6 |
해결방법) 먼저, 데이터가 입력되어있는 배열을 A[], 찾고자 하는 값을 k, upper bound를 찾고자 하는 구간을 [s,e]로 설정하면, 구간 내의 중간 위치를 m이라고 생각할 때, A[m-1] <= k 이면서 A[m]>k를 만족하는 최소 m을 찾는 문제가 된다.
1 3 5 7 7 에서 5를 탐색할 때, 5를 초과하는 첫 위치가 바로 upper bound!
이때, m은 2이상인 값이 되는데, 일반적인 이분탐색에서 A[m] == k인 부분을 포함시키면 된다. 또한, 모든 원소가 k보다 작을 때에는 n+1을 출력해야 하므로, 처음 구간을 잡을 때, [1, n]을 잡는 대신 [1, ㅜ+1]을 잡아야 한다.
데이터를 A[] 배열에 모두 입력 받은 상태에서 탐색 준비를 한다. 찾아야할 데이터 k(7)보다 큰 가장 첫 위치 upper bound, 탐색 구간의 시작위치와 마지막 위치는 s=0, e=7+1이 되고, 중간 위치를 계산하면 (0+8)/2 = 4가 된다.
A[4]의 값(7)이 찾고자 하는 7과 같으므로 탐색 구간을 [5, 8]로 바꾼다. 일반적인 이분탐색이었다면 바로 탐색을 종료해야 하지만, upper bound는 k를 초과하는 위치이므로 위치 4를 포함시킬 필요가 없다. [5,8]의 중간 위치는 (5+8)/2=6이 된다.
A[6]의 값(11)이 찾고자 하는 7보다 크므로 탐색 구간을 [5,6]으로 바꾸고 재탐색한다. [5,6]의 중간위치는 (5+6)/2=5가 된다. A[5]의 값(7)이 찾고자 하는 7과 같으므로 탐색 구간을 [6,6]으로 바꾸고 재탐색 한다. [6,6]의 중간위치는 (6+6)/2=6이 된다.
[6,6]의 범위는 더 이상 탐색할 필요가 없으므로, 6번의 위치가 upper bound라는 것을 판단할 수 있다.
int solve(int s, int e) { int m; while(e-s > 0) { m = (s+e)/2; if(A[m] <= k) s=m+1; else e=m; } return e+1; }
4: 주어진 범위 [s,e]에서 탐색하도록 한다. s==e이면 반복 종료
6: 주어진 범위의 중간 위치를 계산한다.
7: 찾고자 하는 값보다 작거나 같으면 오른쪽으로 한 칸 더 시작 구간 갱신(lower bound에서는 A[m]<k)
12: 찾고자 하는 값보다 크면 거기까지 끝 구간 갱신
14: 찾는 구간에 없는 경우 가장 마지막+1 위치 전달
Upper_bound(): <algorithm>에 포함되어있는 함수로, n개의 데이터로 채우진 A[] 배열에서 비교기준(compare)에 따라서 Upper_bound의 위치를 계산해 준다.
std::upper_bound(A, A+n, k, [compare])
[compare] 부분을 생략하는 경우에는 기본적으로 오름차순으로 정렬한 상태라고 가정했을 때의 upper bound 위치를 계산해 준다.
#include <stdio.h> #include <algorithm> int n, k, A[1000001]; int main() { scanf("%d", &n); for(int i=0; i<n; i++) scanf("%d", A+i); scanf("%d", &k); printf("%d\n", std::upper_bound(A, A+n, k)-A+1); }
12: std::upper_bound(Am A+n, k)는 어떤 위치, std::upper_bound(A, A+n, k)-A+1은 A 배열에서의 상대적 위치(인덱스)
lower bound는 정렬되어있는 데이터 집합에서 k값 이상이 처음발견되는 위치를 의미하고, upper bound는 k값을 초과하는 값이 처음 발견되는 위치를 의미합니다.
'프로그래밍(TA, AA) > 알고리즘' 카테고리의 다른 글
[알고리즘] 비선형구조의 탐색, 그래프의 구현 (0) | 2017.05.19 |
---|---|
[알고리즘] 깊이우선 탐색, 너비우선 탐색 (0) | 2017.05.19 |
[알고리즘] 정보과학과 문제, 선형구조의 탐색 (0) | 2017.05.18 |
[알고리즘] 코딩인터뷰 알고리즘 참고사이트 (0) | 2017.05.18 |
[알고리즘] 개인 알고리즘 코드 저장소 (0) | 2017.05.11 |