Baekjoon

[백준] 1456번 - 거의 소수

yujin0517 2023. 2. 4. 23:21

1456번 - 거의 소수

 

<문제>

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

 

1456번: 거의 소수

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

www.acmicpc.net

 

<문제 풀이>

먼저 정해진 범위 내에서 소수만 저장된 List를 구한다. 

public static void makePrimeArr(long end) { // 소수로만 이루어진 List 생성
	arr = new long[(int) (end + 1)];

	for (int i = 2; i <= end; i++) {
		arr[i] = i;
	}

	for (int i = 2; i * i <= end; i++) {
		if (arr[i] == 0)
			continue;
		for (int j = i * i; j <= end; j += i) {
			arr[j] = 0;
		}
	}

	for (int i = 2; i <= end; i++) {
		if (arr[i] != 0) { // 소수이면 추가
			prime.add(arr[i]);
		}
	}
}

 

1차 시도

N은 2 이상이기 때문에 B의 제곱근을 사용할 소수의 범위로 지정한다. 만약 B가 1000이면, 2~31까지의 범위에 포함되는 소수를 사용하여 거의 소수를 구한다. 

소수만 저장된 List에 요소를 N 제곱해서 A ~ B 범위에 부합하는지 확인한다. 범위에 부합하면 거의 소수의 개수를 확인하는 변수를 1씩 증가시킨다. 

for문을 통해 List를 순회하고 나면 N을 1씩 증가시켜 List의 요소가 N 제곱하여도 A ~ B 범위에 부합하는지 확인한다.

만약 이 범위에 부합하지 않는다면 List에서 해당 요소를 제거한다. 

List의 크기가 0이 되면 이 과정을 종료하고 거의 소수를 저장한 변수를 출력한다. 

while (true) {
	if (prime.size() == 0) break; // prime의 길이가 0이되면 반복문 종료

	for (int i = 0; i < prime.size(); i++) {
		long tmp = (long) Math.pow(prime.get(i), N);
		if (tmp <= B) {
			if (tmp >= A) cnt++; //거의 소수 개수 증가
		} 
        else if (tmp > B) {
			prime.remove(i); // prime.get() ^ N의 수가B를 넘어가면 prime에서 해당 소수를 삭제
		}
	}
    
	N++; // N을 1씩 증가
}

하지만 이 방법은 시간초과로 실패...

 

2차 시도

makePrimeArr 메소드를 거쳐 만들어진 prime 리스트를 가지고 B의 범위만 고려하여 새로운 List를 생성한다. 

이 과정을 마치고 나면 이진 탐색을 사용하여 A의 범위도 고려하여 거의 소수의 개수를 구한다. 

1차 시도에서 걸렸던 시간 초과 문제를 이진 탐색으로 해결! 

 

<코드>

더보기
import java.io.*;
import java.util.*;

public class Main {
	static long A, B;
	static ArrayList<Long> prime = new ArrayList<>();
	static long[] arr;
	static long cnt = 0, N = 2;

	public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		StringTokenizer st = new StringTokenizer(br.readLine());
		A = Long.parseLong(st.nextToken());
		B = Long.parseLong(st.nextToken());

		// B의 제곱근을 사용하여 N제곱하긴 전의 소수의 범위를 지정
		// B=1000 -> 제곱근B = 31.xxx
		// 소수의 범위는 2 ~ 31 : 2,3,5,7,11,13,17,19,23,29,31
		// 각각의 소수에 몇 제곱을하여 B를 넘어가지 않는 거의 소수의 개수를 찾으면 됨

		long b = (long) Math.sqrt(B);
		makePrimeArr(b); // 소수List 생성

		ArrayList<Long> list = new ArrayList<>();
		//B의 범위만 고려하여 거의 소수를 구함.
		for (int i = 0; i < prime.size(); i++) {
			if (prime.get(i) >= B) //범위를 넘어감.
				break;

			for (int j = 2; true; j++) {
				if ((long) Math.pow(prime.get(i), j) > B)
					break;
				list.add((long) Math.pow(prime.get(i), j));
			}
		}

		//A의 범위도 고려하여 거의 소수를 구함. -> Binary Search
		Collections.sort(list);
		int s = 0;
		int e = list.size() - 1;
		while (s <= e) {
			int mid = (s + e) / 2;
			if (list.get(mid) >= A)
				e = mid - 1;
			else
				s = mid + 1;
		}

		System.out.println(list.size() - s);
		// 두 번째 오류 -> 시간초과: 이진탐색으로 해결!
	}

	public static void makePrimeArr(long end) { // 소수로만 이루어진 List 생성
		arr = new long[(int) (end + 1)];

		for (int i = 2; i <= end; i++) {
			arr[i] = i;
		}

		for (int i = 2; i * i <= end; i++) {
			if (arr[i] == 0)
				continue;
			for (int j = i * i; j <= end; j += i) {
				arr[j] = 0;
			}
		}

		for (int i = 2; i <= end; i++) {
			if (arr[i] != 0) { // 소수이면 추가
				prime.add(arr[i]);
			}
		}
	}
}

 

<결과>

 

 

2022.02.04

'Baekjoon' 카테고리의 다른 글

[백준] 2178번, 2667번  (0) 2023.02.12
[백준] 1644번 - 소수의 연속합  (0) 2023.02.05
[백준 DP] 9251번 - LCS  (0) 2023.01.04
[백준 DP] 2293번 - 동전 1  (0) 2023.01.04
[백준 DP] 11048번 - 이동하기  (0) 2022.12.28