
중요한 점
어떤 것을 기준으로 범위를 좁힐 것인가? (변수 설정)
이 문제에서는 숫자 카드 2 문제에서 배웠던 Upper Bound의 개념을 사용해야 합니다. 그 이유는 Lower Bound와 Upper Bound가 중복 원소가 있을 때 특정 값의 위치를 찾는 데 사용되기 때문입니다.
이진 탐색의 목적은, 특정 값에 대한 배열의 특정 인덱스를 찾기 위함입니다. 이를 확장하면, 특정 조건을 만족하는 최대/최소값을 쉽게 찾을 수 있습니다.
중복 원소의 개수를 구하기 위해 Array를 정렬한 후, key 값과 비교해가며 Upper Bound와 Lower Bound를 구해 두 값의 차이를 통해 중복 원소의 개수를 구했습니다.
- Upper Bound는 배열 내에서 key 값을 초과하는 값이 처음으로 나타나는 위치를 찾습니다. (Upper Bound - 1을 해줘야 key 값을 얻을 수 있음)
중복된 갯수가 있을 때, 얻을 수 있는 최대 길이를 찾는 문제입니다.
결국 리턴받아야 하는 것은 길이에 대한 정보이기 때문에, 탐색해야 할 범위는 길이가 됩니다. 그러므로, mid, max, min은 길이에 대한 변수입니다.
다음과 같이 작성할 수 있습니다:
// min의 초기값: 0, max: 입력받은 랜선 중 제일 큰 값
while (min < max) {
mid = (min + max) / 2;
long count = 0;
for (int i = 0; i < arr.length; i++) {
count += (arr[i] / mid);
} // 해당 mid으로 구할 수 있는 count 누적 합
if (count < N) {
max = mid; // count < N인 경우에 mid의 길이를 줄이기
} else {
min = mid + 1; // count가 중복되는 경우에, mid 값을 1씩 늘려가면서 최대값 찾기
}
}
import java.util.Scanner;
public class Test {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int N = in.nextInt();
int M = in.nextInt();
int Arr[] = new int[N];
long max = 0;
for (int i = 0; i < N; i++) {
Arr[i] = in.nextInt();
if (max < Arr[i]) {
max = Arr[i];
}
}
long min = 0;
long mid = 0;
max++;
while (min < max) {
long count = 0;
mid = (min + max) / 2;
for (int i = 0; i < Arr.length; i++) {
count += (Arr[i] / mid) ;
}
if (count < M) {
max = mid;
} else {
min = mid + 1;
}
}
System.out.println(min-1);
}
}