시간 제한 2초
세준이는 크기가 N×N인 배열 A를 만들었다. 배열에 들어있는 수 A[i][j] = i×j 이다. 이 수를 일차원 배열 B에 넣으면 B의 크기는 N×N이 된다. B를 오름차순 정렬했을 때, B[k]를 구해보자.
배열 A와 B의 인덱스는 1부터 시작한다.
첫째 줄에 배열의 크기 N이 주어진다. N은 105보다 작거나 같은 자연수이다. 둘째 줄에 k가 주어진다. k는 min(109, N2)보다 작거나 같은 자연수이다.
B[k]를 출력한다.
3
7
6
문제 설명은 이해가 되었는데 풀이 과정이 이해하기 힘든 문제였던 것 같다. 특히 이진 탐색으로 접근해야 한다는 생각까지 정말 오랜 시간이 걸렸던 것 같다.
여러 블로그와 책을 통해 그나마 정리한 내용을 작성해보기로 하였다.
우선 예제 입력 1 에 입력된 데이터를 그림으로 표현해보자면
3 X 3 크기의 배열을 일차원 배열에 넣어준 후 오름차순으로 정렬 후 K번째 수를 구하면 되는 문제이다. 하지만 N은 최대 이며 이는 전체 배열의 크기는 * 으로 최대 이다. 이는 시간복잡도가 인 알고리즘을 사용할 수 없다는 것을 파악할 수 있다.
그렇다면 어떠한 방식으로 풀어야 하는가? 우선 B[k] 의 의미를 파악해보자
반복되어 있는 식들을 살펴보면 B[K] = x 라는 의미는 x 보다 작거나 같은 원소의 개수가 최소 k 개이다.
그림으로 표현하자면 B[5] = 3 은 3보다 작거나 같은 원소의 개수가 최소 5 개 이다.
그렇다면 현재 찾고자 하는 결과값은 B[K] 를 출력하는 것인데 K는 이미 문제에서 주어진 상태이니깐 임의의 x 값을 이용해서 x 보다 작거나 같은 원소의 개수가 K 값과 일치하는지를 확인하면 된다.
이때 이진탐색을 사용하여서 중앙값보다 작은 수의 개수를 세어보면서 범위를 절반씩 줄여가가며 B[K]를 찾는것이 이 문제의 핵심이라고 할 수 있다.
그러면 예제 입력 1을 이용해서 차근 차근 이진탐색으로 문제를 풀어보겠다.
현재 입력된 N = 3, K = 7 이다. 2차원 배열에서 K 번째 수는 1 ~ K번째 안에 정답이 있다고 할 수 있다. 시작 인덱스를 1, 종료 인덱스를 7로 정하고 중앙값인 (1 + 7) / 2 = 4 를 구한다.
중앙값보다 작거나 같은 수의 개수는 중앙값을 N으로 나눈 값이다. 단, 나눈 값이 N 보다 크면 N으로 정합니다.
그렇다면 중앙값 4는 6번째 수보다 큰 수가 될 수 없기 때문에 중앙값 4보다 더 큰 범위에 정답이 있다는 것을 알 수 있다.
마지막 과정에서 시작 인덱스가 종료 인덱스보다 크므로 이진 탐색을 종료하고 정답값을 저장한 6이 바로 우리가 찾고자 하는 값이다.
이를 코드에 적용하면 다음과 같다.
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class 백준_K번째수 {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int N = Integer.parseInt(br.readLine());
int K = Integer.parseInt(br.readLine());
// x 는 1부터 K 사이의 범위를 가진다.
long start = 1;
long end = K;
while(start < end) {
long mid = (start + end) / 2; // 중앙값을 구한다.
long count = 0;
/*
중앙값보다 작은 수는 몇 개인지 계산한다.
중앙값을 i 번 째 행으로 나누면서 누적 합을 구한다.
*/
for(int i = 1; i <= N; i++) {
count += Math.min(mid / i, N);
}
/*
중앙값보다 작은 수의 개수가 K 보다 작다면
- 시작 인덱스 = 중앙 인덱스 + 1
중앙값보다 작은 수의 개수가 K 보다 크거나 같으면
- 종료 인덱스 = 중앙 인덱스 - 1
*/
if(K <= count) {
end = mid;
}else {
start = mid + 1;
}
}
System.out.println(start);
}
}
수학적 지식이 부족해서 그런지 문제를 풀었음에도 내가 나중에 설명할 자신이 없는 문제다. 밑에 참고한 블로그와 책을 여러번 보면서 어느정도 풀었지만 아직 부족하다. 역시 코딩테스트 문제는 하루에 한번씩은 계속 풀어봐야 하는 것 같다.
참고한 블로그 : https://st-lab.tistory.com/281