새소식

알고리즘/문제풀이

백준 - 1456: 거의 소수

  • -

문제


 

1456번: 거의 소수

어떤 수가 소수의 N제곱(N ≥ 2) 꼴일 때, 그 수를 거의 소수라고 한다. 두 정수 A와 B가 주어지면, A보다 크거나 같고, B보다 작거나 같은 거의 소수가 몇 개인지 출력한다.

www.acmicpc.net

 

풀이


💡 제가 푼 방법보다 더 좋은 방법이 있을 수 있습니다. 이렇게 푸는게 항상 정답은 아니니 참고하셔야 합니다.

 

범위는 1 ~ 10¹로 입력값을 담는 타입은 long을 이용해야 합니다.

BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

StringTokenizer st = new StringTokenizer(br.readLine());

long A = Long.parseLong(st.nextToken());
long B = Long.parseLong(st.nextToken());

 

우선 소수를 구해야 거의 소수를 알 수 있기 때문에 소수부터 구해봅시다.

 

구해야되는 소수의 범위는 1 ~ B라고 생각할 수 있지만, 문제에서 구하라는 것은 '거의 소수'입니다.

소수의 n제곱수들을 구하는 것이기 때문에 구할 수 있는 소수 중에 가장 큰 소수의 제곱이 B를 넘지 않아야합니다.

 

식으로 나타내면 다음과 같습니다.

  • p : 소수
  • ≤ B → P ≤ √B

 

소수를 구하려는 범위가 정해졌다면, 에라토스테네스의 체를 이용하여 아래와 같이 소수를 구합니다.

public static boolean[] findPrime(long n) {

   // 입력 값의 최대치인 10^14를 루트를 씌우면 10^7이므로 int 범위 내라 배열 크기로 잡을 수 있음
   int range = (int)Math.sqrt(n);

   boolean[] noPrime = new boolean[range + 1];

   noPrime[0] = noPrime[1] = true;

   for (int i = 2; i <= Math.sqrt(range); i++) {
      if (!noPrime[i]) {
         for (int j = i + i; j <= range; j += i) {
            noPrime[j] = true;
         }
      }
   }

   return noPrime;
}

 

소수를 구했다면, 소수의 n제곱수들을 구해야할 차례입니다.

 

소수의 n제곱 값이 long타입을 벗어날 수 있으니 이항정리를 통해 식을 정리하여 넘지 않아도 구할 수 있도록 해줍니다.

boolean[] noPrime = findPrime(B);

int cnt = 0;

// 거의 소수를 구하기 위해 구한 소수들을 순회
for (int i = 2; i < noPrime.length; i++) {
   // 소수라면
   if (!noPrime[i]) {
      long nearPrime = i;

      /**
       *     소수의 n제곱 값이 long타입을 벗어날 수 있으므로 이항 정리로 정리하여 처리합니다.
       *     A <= p^n <= B , n >= 2
       *     -> A/p^(n-1) <= p <= B/p^(n-1) , n >= 2
          */
      while ((double) i <= (double) B / (double) nearPrime) {
         if ((double) i >= (double) A / (double) nearPrime) {
            cnt++;
         }

         nearPrime *= i;
      }
   }
}

 

전체 코드는 다음과 같습니다.

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;


public class Main {

   public static void main(String[] args) throws IOException {

      BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

      StringTokenizer st = new StringTokenizer(br.readLine());

      long A = Long.parseLong(st.nextToken());
      long B = Long.parseLong(st.nextToken());

      boolean[] noPrime = findPrime(B);

      int cnt = 0;

      // 거의 소수를 구하기 위해 구한 소수들을 순회
      for (int i = 2; i < noPrime.length; i++) {
         // 소수라면
         if (!noPrime[i]) {
            long nearPrime = i;

            /**
             *     소수의 n제곱 값이 long타입을 벗어날 수 있으므로 이항 정리로 정리하여 처리합니다.
             *     A <= p^n <= B , n >= 2
             *     -> A/p^(n-1) <= p <= B/p^(n-1) , n >= 2
             */
            while ((double) i <= (double) B / (double) nearPrime) {
               if ((double) i >= (double) A / (double) nearPrime) {
                  cnt++;
               }

               nearPrime *= i;
            }
         }
      }
      System.out.println(cnt);
   }

   public static boolean[] findPrime(long n) {

      // 입력 값의 최대치인 10^14를 루트를 씌우면 10^7이므로 int 범위 내라 배열 크기로 잡을 수 있음
      int range = (int)Math.sqrt(n);

      boolean[] noPrime = new boolean[range + 1];

      noPrime[0] = noPrime[1] = true;

      for (int i = 2; i <= Math.sqrt(range); i++) {
         if (!noPrime[i]) {
            for (int j = i + i; j <= range; j += i) {
               noPrime[j] = true;
            }
         }
      }

      return noPrime;
   }
}

'알고리즘 > 문제풀이' 카테고리의 다른 글

백준 - 10869번 사칙연산  (0) 2023.07.13
백준 - 1008번 A/B  (0) 2023.07.13
백준 - 1001번 A-B  (0) 2023.07.12
백준 - 1000번 A+B  (0) 2023.07.12
백준 - 2557번 Hello World  (0) 2023.07.12
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.