상세 컨텐츠

본문 제목

[JAVA] 백준 1557번 제곱ㄴㄴ

알고리즘

by dajingjing 2022. 4. 14. 14:14

본문

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

 

1557번: 제곱 ㄴㄴ

어떤수 N이 1이 아닌 제곱수로 나누어지지 않을 때, 이 수를 제곱ㄴㄴ수라고 한다. 제곱수는 4, 9, 16, 25와 같은 것이고, 제곱ㄴㄴ수는 1, 2, 3, 5, 6, 7, 10, 11, 13, ...과 같은 수이다. K가 주어졌을 때, K

www.acmicpc.net

이전 백준 문제는 화면을 캡쳐하고 몇번째 줄은 어떤 의미인지 설명했는데,

이 문제는 다시 풀 때 캡쳐해서 줄별로 설명하지 않고 의식의 흐름대로 주석으로 정리하면서 풀어보았다.

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

public class Main {
    static int max = 100000;
    static List<Integer> list = new ArrayList<>();

    static int getSqureCnt(int index, long num, long tempAns){
        if(index>=list.size()){
            return 0;
        }
        if(num*list.get(index) > tempAns){
            return 0;
        }
        int ans = 0;
        ans += tempAns/(num*list.get(index));
        ans += getSqureCnt(index+1, num, tempAns);
        ans -= getSqureCnt(index+1, num*list.get(index), tempAns);
        return ans;
    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int k = sc.nextInt();   //정수 K번째에 있는 제곱배수가 아닌 수 구하기
        // K = 10 -> 1,2,3,5,6,7,10,11,13,14
        // n = 14 라하면, 14 안에는 제곱배수가 4,8,12, 9 이므로 14-4 = 10
        // k가 10일때 정답은 14.
        // 14를 먼저 구하고 14 안에 있는 제곱수의 배수의 개수를 빼준 것이 답이다.

        //제곱수를 먼저 구한다.
        //제곱수는 4, 9, 16, 25... 16은 4의 제곱수의 배수이므로 제외해야한다.
        //소수의 제곱수를 구해야 중복되는 값이 제외된다.
        //소수가 아닌 수(1이 아닌 다른 수로 나누어지는 수)의 제곱은
        //-이미 이전에 구한 제곱수의 배수가 되므로 제외할 수 있다.
        //예를들어, 4,6,8은 2의 배수이므로 4,6,8의 제곱수는 2의 제곱수의 배수가 되므로 제외한다.
        boolean[] d = new boolean[max+1];
        for(int i=2; i<=max; i++){
            int square = i*i;
            if(d[i]) continue;
            for(int j=i; j<=max; j+=i){
                d[j] = true;
            }
            list.add(square);
        }

        //k번째 제곱ㄴㄴ수를 구하기 위해서는 ans값을 먼저 알아야한다.
        //ans를 알아야 그 안에 제곱수의 배수의 수를 구하고 ans가 몇번째 위치한 수(k)인지 알수있다.
        //이분탐색을 이용한다.
        long left = 0;
        long right = Integer.MAX_VALUE;
        long ans = 0;
        while(left < right){
            long mid = (left + right) / 2;
            int tempAns = (int)mid - getSqureCnt(0, 1, mid);
            if(tempAns < k){
                left = mid + 1;
            }else{
                ans = mid;
                right = mid;
            }
        }
        System.out.println(ans);
        sc.close();
    }

}

이분탐색을 이용하는 while(left<right)부분은 left만 mid+1 인 부분이 이분탐색을 처음 접했던 때는 참 헷갈렸었다.

3을 2로 나눠도 mid=1이고 2를 2로 나눠도 mid=1이 나오니, left값은 mid+1을 해줬고

while(left<=right)를 한다면 left=mid+1, right=mid-1 해주면 된다.

'알고리즘' 카테고리의 다른 글

[JAVA] 백준 1201번 NMK  (0) 2022.05.02
[JAVA] 백준 1767번 N-Rook II  (0) 2022.04.16
[JAVA] 백준 1016번 제곱ㄴㄴ수  (0) 2022.04.14
[JAVA] 백준 1285번 동전 뒤집기  (0) 2022.04.13
[JAVA] 백준 1629번 곱셈  (0) 2022.04.11

관련글 더보기