문제 풀이/백준

[백준] 1300번. K번째 수(JAVA)

27200 2025. 6. 11. 20:56

문제

https://www.acmicpc.net/problem/1300


풀이(1시간29분)

import java.io.*;
import java.util.*;

public class Main {

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int n = Integer.parseInt(br.readLine());  // n x n 크기의 곱셈표
        int k = Integer.parseInt(br.readLine());  // k번째로 작은 수를 찾기

        long left = 1;
        long right = k;

        // 이진 탐색으로 k번째 수를 찾는다 (lower bound)
        while (left < right) {
            long mid = (left + right) / 2;

            long count = 0;
            // 곱셈표의 각 행에서 mid 이하인 수의 개수를 누적
            for (int i = 1; i <= n; i++) {
                // i행에서 mid 이하인 수의 개수는 mid / i,
                // 단, 한 행의 최대 열 개수는 n
                count += Math.min(mid / i, n);
            }

            // count가 k 이상이면 mid는 k번째 수 이상일 수 있음 → 더 작은 수 가능
            if (count >= k) {
                right = mid;
            } else {
                left = mid + 1;
            }
        }

        // left는 k번째로 작은 수
        System.out.println(left);
    }
}

문제 풀이 전략

 

  1 2 3 4
1 1 2 3 4
2 2 4 6 8
3 3 6 9 12
4 4 8 12 16

 

이 표를 그려놓고 문제를 풀었다.

 


💡 약수를 이용하면 되지 않을까?

첫 번째 아이디어는 약수를 이용하는 것이었다.

예를 들어 생각해 보자.

출처 :https://www.google.com/imgres?q=%EC%95%BD%EC%88%98%EC%9D%98%20%EA%B0%9C%EC%88%98%20%EC%84%B8%EA%B8%B0&imgurl=https%3A%2F%2Fi.ytimg.com%2Fvi%2FtAEMjkHJaI4%2Fmaxresdefault.jpg&imgrefurl=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DtAEMjkHJaI4&docid=ar1M-HM1s4nn-M&tbnid=Qv1G5SJCMJkXwM&vet=12ahUKEwjKuYbipOmNAxXLhq8BHeW_CowQM3oECBsQAA..i&w=1280&h=720&hcb=2&ved=2ahUKEwjKuYbipOmNAxXLhq8BHeW_CowQM3oECBsQAA

이러한 공식을 기억하는가? 중고등학교 시절에 약수의 개수를 구하기 위해 매우 많이 사용했던 공식이다.

이를 위해서는 소인수분해를 거쳐야 했고, n = 10^5까지 가능한 문제 특성상 너무나도 많은 연산을 요구하기에 포기했다.

 

하지만, 얻은 점이 있었다.

분명한 건 어떻게든 약수에 관한 공식을 찾아야 한다는 것과, 약수를 통해 계산하다 보면 초과하는 부분(아래 설명하겠다.)이 필연적으로 발생하는데 이 경우 최대 N으로 제한을 걸면 되겠구나!라는 것이다.


💡 분명 약수를 이용해야 한다.

 

약수에 집착했던 이유는 너무나도 큰 문제 조건을 보았을 때 이분탐색을 이용할 것이라는 게 본능적으로 느껴졌기 때문이다. 그렇다면 나열은 아닐 것이고... 어떤 식으로든 근거가 있다는 뜻이다.

 

그렇기에 생각해 낸 것이 B[k] = x 라는 것인데 여기서 x이하의 개수는 몇 개인지 알 수 있는 방법이 없을까?라는 생각을 했다.

최종적으로 도출해 낸 아이디어는 다음과 같다.

  1 2 3 4
1 1 2 3 4
2 2 4 6 8
3 3 6 9 12
4 4 8 12 16

 

이 표를 다시 돌이켜보며 9 이하는 몇 개의 숫자가 있는지 생각해 보자.

1단에서 4개, 2단에서 4개, 3단에서 3개, 4단에서 2개가 있다.

느낌이 오는가?

1단의 경우 -> 9/1 = 9 -> 즉, 9 이하의 숫자가 9개 있을 수 있다.

2단의 경우 -> 9/2 = 4.5 -> 즉, 9 이하의 숫자가 4개 있을 수 있다.

3단의 경우 -> 9/3 = 3 -> 즉, 9 이하의 숫자가 3개 있을 수 있다.

4단의 경우 -> 9/4 = 2.25 -> 즉, 9 이하의 숫자가 2개 있을 수 있다.

나눗셈을 생각하면 꽤나 당연한 이야기이다.

 

그렇다면 위에 초과하는 부분(1단의 경우 9개)을 모두 세면 안 된다. 왜냐하면 N = 4로 제한이 걸려있기 때문이다. 그렇기에 

count += Math.min(mid / i, n);

연산이 필요하다. 만약 n을 넘는다면 최대 n개만 사용하겠다는 의미이다.

 

최종적으로 이를 이분탐색으로 찾아주면 된다!

 

여기서 right = k를 사용하는 이유는 n*n을 써도 되지만 k를 조정하다 보면 어차피 정답은 k이하겠구나라는 것을 알게 됐기에 사용하였다.