[Do It! 코딩테스트 자바편(김종관)] 문제 031(백준: 1300번)
난이도
- 문제 접근하는 아이디어와 관점을 떠올리기 힘들었고, 책 설명을 봐도 이해하는 데 시간 걸렸다.
- 교재 설명이 애매하고 구체적으로 설명하지 않아서, 다른 블로그 설명을 찾아봐서 참고했다.
문제
배열에서 k번째 수 찾기(Baekjoon: 1300번)
풀이
import java.util.Scanner;
public class test {
public static void main(String[] args) throws Exception {
Scanner sc = new Scanner(System.in);
int N = sc.nextInt();
int K = sc.nextInt();
long start = 1, end = K;
long ans = 0;
// 이분 탐색 수행
while (start <= end) {
long middle = (start + end) / 2;
long cnt = 0;
// 중간 값보다 작거나 같은 수는 몇 개인지 계산.
for (int i = 1; i <= N; i++) {
cnt += Math.min(middle / i, N);
}
if (cnt < K) {
start = middle + 1;
} else {
ans = middle; // 현재 단계의 중간 값을 정답 변수에 저장
end = middle - 1;
}
}
System.out.println(ans);
}
}
이해하기 어려웠던 부분
첫 번째
- 시작 인덱스를 1로, 종료 인덱스를 k로 설정해서 오름차순으로 정렬한 B 배열을 탐색한 까닭은 교재에 매모했으니 참고
두 번째
if (cnt < K) {
start = middle + 1;
} else {
ans = middle; // 현재 단계의 중간 값을 정답 변수에 저장
end = middle - 1;
}
- if부분 이해(중간값보다 작거나 같은 수(cnt)가 K보다 작은 경우)
- ex) k=7이라고 하자. 그리고 k번째 수(
B[k]
)인지 테스트하고 싶은 수(middle)이 4라고 치자. 또 4보다 작거나 같은 것의 개수 cnt=6이라고 하자. 이 땐 1부터 4를 포함해서 4까지 카운트한 개수가 6개란 소리다. 즉 B배열에 인덱스 1~6까지 4보다 작거나 같은 수가 들어가 있고, 인덱스 7(찾고자 하는 k번째)에는 4보다 큰 다른 수가 들어있다고 해석 가능하다. - 따라서 우리가 찾고 싶은 k번째 수는 4보다 더 오른쪽에 있다는 소리니까 start값 늘려서, 다시 반복할 때 더 큰 middle값을 가지고 이게 k번째 수인지 확인할 수 있도록 한다.
- ex) k=7이라고 하자. 그리고 k번째 수(
- else부분 이해(중간값보다 작거나 같은 수(cnt)가 K와 같거나 큰 경우)
- ex) k=7이라고 하자. 그리고 k번째 수(
B[k]
)인지 테스트하고 싶은 수(middle)이 6이라고 치자. 또 6보다 작거나 같은 것의 개수 cnt=8이라고 하자. 이 땐 B배열에 인덱스 1~8까지 6보다 작거나 같은 수가 들어가 있다. 하지만 cnt가 딱 k와 같지 않고 큰 경우, 인덱스 7(k번째)에는 5라는 수가 있고, 인덱스 8에 6인 경우와 같은 예외 상황이 생긴다. - 그럼 무턱대고 6을 7번째 수라고 결론지으면 안 된다. end를 줄여서 다음에 반복할 때 middle값을 더 낮춘다. 그리고 계속 탐색하면서 인덱스 7(k번째)에는 5라는 수가 있고, 인덱스 8에 6인 경우같은 상황이 있는지 파악한다. 끝까지 탐색할 때 동안(start<=end인 동안) 계속 cnt값이 k보다 작아서 계속 start 오른쪽으로 움직여서 계속 더 큰 middle값을 탐색해도 안된다. 이런 와중에 끝까지 탐색하면 다음과 같은 결론을 낼 수 있다.
- 인덱스 7(k번째)에는 5라는 수가 있고, 인덱스 8에 6인 경우같은 상황이 있는지 전부 파악해보니 이런 경우는 없었다. 즉 위 그림처럼 인덱스 7과 8에 6이 들어있는 경우라고 결론지을 수 있다. 그럼 인덱스 7(k번째 수)는 6이라고 할 수 있다.
- ex) k=7이라고 하자. 그리고 k번째 수(