백준 단계별로 풀어보기

(C++) 백준 1644 : 소수의 연속합

얼음물수증기 2021. 12. 20. 22:56

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

 

1644번: 소수의 연속합

첫째 줄에 자연수 N이 주어진다. (1 ≤ N ≤ 4,000,000)

www.acmicpc.net

 이 문제는 연속된 부분합을 구하는 문제와 거의 유사한 문제입니다. 다른점은 어떠한 리스트가 주어지고 그 리스트에서의 부분합을 구하는게 아니라는 점입니다. 이 문제에서는 소수를 직접 구하고, 구한 소수를 이용해야합니다. 따라서 이 문제는 소수를 구하기, 연속된 부분합이 N이 되는 경우의수 구하기로 나눌 수 있습니다. 

 

문제 풀이 

 먼저 필요한 소수를 구해야 하는데 어디까지 구해야 하는지 생각해 보아야 합니다. 연속된 소수의 부분합이 N이 되는 경우의 수를 구해야 하는데 이 말인 즉슨 N까지의 소수를 모두 구해야 한다는 말이됩니다. 즉, 소수를 N보다 작게 구하면 안되고 N보다 크게 구할 필요도 없습니다. 만약 N보다 작은 소수만을 구한다면 구하려는 수 N이 소수일 때 무조건 문제가 생기게 됩니다. 소수 N 하나로 이루어진 부분합이 제외되기 때문입니다. N보다 큰 소수들의 합으로는 당연히 N을 만들지 못하므로 N보다 큰 소수도 구할 필요가 없습니다. 

 소수를 구하는 방식은 여러가지가 있는데 에라토스테네스의 체를 사용하면 N까지의 모든 소수를 쉽게 구할 수 있습니다. 소수의 배수를 통해 소수가 아닌 수들을 모두 지워서 소수를 찾는 방식입니다. 자세한 설명은 링크를 통해 확인할 수 있습니다. 

 

void Eratos(int n)
{
	for (int i = 2; i <= n; i++)
	{
		if (!isPrime[i]) continue;
		for (int k = i + i; k <= n; k += i)
			isPrime[k] = false;
	}

 에라토스테네스의 체 알고리즘을 이용하여 isPrime에 N까지의 모든 소수가 아닌 수를 false로 만들어 주었습니다. 이후 isPrime이 true인 값들만 배열에 넣어주어서 이용하면 됩니다.

 

 다음은 이 소수들을 이용해 부분합이 N이되는 경우의 수를 구해야합니다. 이 부분은 투포인터를 사용해 풀 수 있습니다. arr가 소수가 들어있는 배열이고 s,e는 각각 배열의 특정 위치를 가리키는 변수이고 sum은 s부터 e까지 값들의 합입니다. 그리고 result가 모든 경우의 수 입니다. 

sum = arr[s];
	while (s < arr.size())
	{
		if (sum > N) sum-=arr[s++];
		else if (sum < N)
		{
			if (++e >= arr.size()) break;
			sum += arr[e];
		}
		else
		{
			result++;
			sum -= arr[s++];
		}
	}

 s와 e는 0부터 시작해서 sum이 N보다 작으면 sum을 더 크게 만들어줘야 하기 때문에 e를 증가시켜서 값을 더해주고, sum이 N보다 크다면 sum을 더 작게 만들어 줘야 하기 때문에 s를 증가시켜 값을 빼줍니다. 만약 sum과 N이 같다면 result를 증가시켜주고 배열 끝까지 이 과정을 반복합니다. 이를 통해 부분합이 N인 모든 경우의 수를 구할 수 있습니다.

 

전체 코드입니다.

#include<iostream>
#include<vector>

bool isPrime[4000001];
std::vector<int> arr;

void Eratos(int n)
{
	for (int i = 2; i <= n; i++)
	{
		if (!isPrime[i]) continue;
		for (int k = i + i; k <= n; k += i)
			isPrime[k] = false;
	}
}

int main() {
	int N,result=0,s=0,e=0,sum;
	std::cin >> N;
	std::fill(isPrime + 2, isPrime + N + 1, true);
	if (N == 1) {
		std::cout << 0;
		return 0;
	}
	Eratos(N);
	for (int i = 2; i <= N; i++)
		if (isPrime[i])
			arr.push_back(i);
	sum = arr[s];
	while (s < arr.size())
	{
		if (sum > N) sum-=arr[s++];
		else if (sum < N)
		{
			if (++e >= arr.size()) break;
			sum += arr[e];
		}
		else
		{
			result++;
			sum -= arr[s++];
		}
	}
	std::cout << result;
	return 0;
}