lower bound
lower bound는 찾고자 하는 값 이상이 처음 나타나는 위치다. lower bound의 경우에는 같은 원소가 여러개 있더라도 상관없다. 찾고자 하는 값 이상의 값이 처음 나타나는 위치를 찾아내기 위해, 이분탐색 방법에서의 조건을 조금 변경하면 된다.
[문제] n개로 이루어진 정수 집합에서 원하는 수 k이상인 수가 처음으로 등장하는 위치를 찾으시오. 단, 입력되는 집합은 오름차순으로 정렬되어 있으며(이분 탐색 가능), 같은 수가 여러개 존재할 수 있다.
입력) 첫 줄에 한 정수 n이 입력된다. 둘째 줄에 n개의 정수가 공백으로 구분되어 입력된다. 셋째 줄에는 찾고자 하는 값 k가 입력된다. (단, 2 <= n <= 1,000,000, 각 원소의 크기는 100,000,000을 넘지 않는다.)
출력) 찾고자 하는 원소의 위치를 출력한다. 만약 모든 원소가 k보다 작으면 n+1을 출력한다.
해결방법) 먼저, 데이터가 입력되어있는 배열을 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이상인 원소의 최초 위치가 된다.
6: 주어진 범위 [s,e]에서 참색하도록 한다. s==e이면 반복 종료
8: 주어진 범위의 중간 위치를 계산한다.
9: 찾고자 하는 값보다 작으면 오른쪽으로 한 칸만 더 시작 구간 갱신
10: 찾고자 하는 값보다 크면 거기까지 끝 구간 갱신
12: 찾는 구간에 없는 경우 가장 마지막+1 위치 전달
lower_bound는 이분탐색과 비슷하지만 끝의 경계조건을 처리하기가 매우 까다롭다. 따라서 실제 정보올림피아드 대회에서는 lower_bound를 활용하기 위해서 stl라이브러리인 알고리즘에 포함된 함수를 사용한다.
#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;
}
lower_bound(): <algorithm>에 포함되어있는 함수로, n개의 데이터로 채워진 A[] 배열에서 비교기준(compare)에 따라서 lower_bound의 위치를 계산해준다.
std::lower_bound(A, A+n, k, [compare])
[compare] 부분을 생략하는 경우에는 기본적으로 오름차순으로 정렬한 상태라고 가정했을 때의 lower bound 위치를 계산해 준다.
12: std::lower_bound(A, A+n, k)는 어떤 위치, std::lower_bound(A, A+n, k)-A+1은 A배열에서의 상대적 위치(인덱스)
#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;
}
upper bound
lower bound는 찾고자 하는 값 이상이 처음으로 나타나는 위치인 반면에, upper bound는 찾고자 하는 값보다 큰 값이 처음으로 나타나는 위치다.
[문제] n개로 이루어진 정수 집합에서 원하는 수 k를 초과하는 수가 처음으로 등장하는 위치를 찾으시오. 단, 입력되는 집합은 오름차순으로 정렬되어 있으며, 같은 수가 여러 개 존재할 수 있다.
입력) 첫 줄에 한 정수 n과 찾고자 하는 값 k가 공백으로 구분되어 입력되고, 둘째 줄에 n개의 정수가 공백으로 구분되어 입력된다. (단, 2 <= n <= 1,000,000, 각 원소의 크기는 100,000,000을 넘지 않는다)
출력) 찾고자 하는 원소의 위치를 출력한다. 만약 모든 원소가 k보다 작으면 n+1을 출력한다.
해결방법) 먼저, 데이터가 입력되어있는 배열을 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, n+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라는 것을 판단할 수 있다.
4: 주어진 범위 [s,e]에서 탐색하도록 한다. s==e이면 반복 종료
6: 주어진 범위의 중간 위치를 계산한다.
7: 찾고자 하는 값보다 작거나 같으면 오른쪽으로 한 칸 더 시작 구간 갱신(lower bound에서는 A[m]<k)
12: 찾고자 하는 값보다 크면 거기까지 끝 구간 갱신
14: 찾는 구간에 없는 경우 가장 마지막+1 위치 전달
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;
}
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값을 초과하는 값이 처음 발견되는 위치를 의미한다.
출처: https://12bme.tistory.com/120 [길은 가면, 뒤에 있다.:티스토리]
'CS > 자료구조 & 알고리즘' 카테고리의 다른 글
에이전트의 정의 (0) | 2024.04.08 |
---|---|
[C++] 라운드 로빈 스케줄링 구현 (우선순위 큐 이용) (0) | 2024.03.06 |
[C++] 계단 오르기 알고리즘 (0) | 2023.12.18 |
sort() 함수에서 쓰여지는 정렬 알고리즘 (Intro 인트로, Tim 팀) (0) | 2023.12.11 |
[C++] Radix Sort (기수 정렬) (0) | 2023.12.04 |