백준 등 알고리즘

[C#] 백준 1644 소수의 연속 합 (투 포인터)

박도치 2024. 11. 11. 20:17

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

 

주어진 수가 연속된 소수의 합으로 나올 수 있는 경우의 수를 구하는 문제이다. 소수를 구하고, 연속된 수의 합으로 입력한 수 N이 몇 개가 나올 수 있는지에 대해 찾으면 된다.

 

문제 해결 순서는 다음과 같다.

 

1. 입력을 받는다.

2. 입력받은 수를 기점으로 소수를 구한다.

3. 소수의 배열을 투포인터를 통해 연속된 합을 구한다.

 

 

투 포인터

투 포인터는 배열이나 리스트를 두개의 포인터(인덱스)를 이용해 탑색하는 방식이다. 연속된 구간 합이나 특정 조건을 만족하는 쌍을 찾을 때 사용된다. 

 

이 방법을 쓰는 이유는 연속된 합을 찾는 것의 단순한 방식은 이중 반복문인데, 이중 반복문은 시간 복잡도가 오래 걸리기 때문에 효율적인 투 포인터 방식을 사용하는 것이다.

 

투 포인터는 start와 end의 두 가지 포인터를 설정하고, 각 포인터에 맞게 이동하여 조건을 탐색하는 방식이다.

 

1. start와 end를 배열의 0번째인 첫 번째에 둔다.

2. 합을 초기화 시킨다.

3. 합이 기준이 되는 수 N보다 작은 경우 end포인터를 이동시켜 sum에 더해준다.

4. 합이 N보다 클 경우 start를 이동시켜 end와 start의 간격을 줄이고, start 위치 값을 합에서 뺀다.

5. 합과 N이 같은 경우 카운트를 증가시킨다.

 

이를 end가 배열의 끝까지 도달할 때 까지 반복한다.

 

요약하자면, N보다 크거나 같아질 때 까지 배열을 연속해서 합하고, 같을 경우 count를 증가, 초과할 경우 start를 증가시켜 배열을 앞으로 당기고, 합에서 start부분을 빼는 것이다.

 

static int TwoPointers(int N, List<int> primes)
{
    int start = 0;
    int sum = 0;
    int count = 0;

    for(int end = 0; end < primes.Count; end ++)
    {
        sum += primes[end];

        while(sum > N && start <= end)
        {
            sum -= primes[start];
            start++;
        }

        if (sum == N)
            count++;

    }

    return count;

}

 

 

예시인 41은 2, 3, 5, 7, 11, 13 의 합인데, 41의 경우 17, 19... 41 까지 소수가 나열되어 있다. 그렇다면 저 수에 17을 더할 경우 41 + 17 = 58, 41 보다 초과가 될 것이다.

 

이렇게 초과 될 경우 start의 포인터를 첫번째에서 앞으로 옮겨주며, 2부터 순차대로 41보다 같거나 작아질 때 까지 빼준다.

 

0 ~ 6 까지 나열되어있던 start와 end포인터의 범위가 1 ~ 6, 2 ~ 6... 이런식으로 줄어들고 다시 늘어나고 하는 식으로 end가 배열 끝에 도달할 때 까지 체크하는 것이다.

 

정답 

using System;
using System.Linq;

class Program
{
    static void Main()
    {
        int N = int.Parse(Console.ReadLine());

        List<int> primes = Prime(N);

        int count = TwoPointers(N, primes);

        Console.WriteLine(count);
    }

    static List<int> Prime(int N)
    {
        bool[] isPrime = new bool[N + 1];
        List<int> primes = new List<int>();

        for(int i = 2; i <= N; i++)
            isPrime[i] = true;

        for (int i = 2; i * i <= N; i ++)
        {
            if (isPrime[i])
            {
                for(int j = i * i; j <= N; j += i)
                {
                    isPrime[j] = false;
                }
            }
        }

        for(int i = 2; i <= N; i ++)
        {
            if (isPrime[i])
            {
                primes.Add(i);
            }
        }
        
        return primes;
    }

    static int TwoPointers(int N, List<int> primes)
    {
        int start = 0;
        int sum = 0;
        int count = 0;

        for(int end = 0; end < primes.Count; end ++)
        {
            sum += primes[end];

            while(sum > N && start <= end)
            {
                sum -= primes[start];
                start++;
            }

            if (sum == N)
                count++;

        }

        return count;

    }
}